roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11916      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556              Roo.log("load records failed");
19557            
19558             
19559             
19560             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19561             return;
19562         }
19563         var r = o.records, t = o.totalRecords || r.length;
19564         if(!options || options.add !== true){
19565             if(this.pruneModifiedRecords){
19566                 this.modified = [];
19567             }
19568             for(var i = 0, len = r.length; i < len; i++){
19569                 r[i].join(this);
19570             }
19571             if(this.snapshot){
19572                 this.data = this.snapshot;
19573                 delete this.snapshot;
19574             }
19575             this.data.clear();
19576             this.data.addAll(r);
19577             this.totalLength = t;
19578             this.applySort();
19579             this.fireEvent("datachanged", this);
19580         }else{
19581             this.totalLength = Math.max(t, this.data.length+r.length);
19582             this.add(r);
19583         }
19584         this.fireEvent("load", this, r, options);
19585         if(options.callback){
19586             options.callback.call(options.scope || this, r, options, true);
19587         }
19588     },
19589
19590
19591     /**
19592      * Loads data from a passed data block. A Reader which understands the format of the data
19593      * must have been configured in the constructor.
19594      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19595      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19596      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19597      */
19598     loadData : function(o, append){
19599         var r = this.reader.readRecords(o);
19600         this.loadRecords(r, {add: append}, true);
19601     },
19602
19603     /**
19604      * Gets the number of cached records.
19605      * <p>
19606      * <em>If using paging, this may not be the total size of the dataset. If the data object
19607      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19608      * the data set size</em>
19609      */
19610     getCount : function(){
19611         return this.data.length || 0;
19612     },
19613
19614     /**
19615      * Gets the total number of records in the dataset as returned by the server.
19616      * <p>
19617      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19618      * the dataset size</em>
19619      */
19620     getTotalCount : function(){
19621         return this.totalLength || 0;
19622     },
19623
19624     /**
19625      * Returns the sort state of the Store as an object with two properties:
19626      * <pre><code>
19627  field {String} The name of the field by which the Records are sorted
19628  direction {String} The sort order, "ASC" or "DESC"
19629      * </code></pre>
19630      */
19631     getSortState : function(){
19632         return this.sortInfo;
19633     },
19634
19635     // private
19636     applySort : function(){
19637         if(this.sortInfo && !this.remoteSort){
19638             var s = this.sortInfo, f = s.field;
19639             var st = this.fields.get(f).sortType;
19640             var fn = function(r1, r2){
19641                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19642                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19643             };
19644             this.data.sort(s.direction, fn);
19645             if(this.snapshot && this.snapshot != this.data){
19646                 this.snapshot.sort(s.direction, fn);
19647             }
19648         }
19649     },
19650
19651     /**
19652      * Sets the default sort column and order to be used by the next load operation.
19653      * @param {String} fieldName The name of the field to sort by.
19654      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19655      */
19656     setDefaultSort : function(field, dir){
19657         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19658     },
19659
19660     /**
19661      * Sort the Records.
19662      * If remote sorting is used, the sort is performed on the server, and the cache is
19663      * reloaded. If local sorting is used, the cache is sorted internally.
19664      * @param {String} fieldName The name of the field to sort by.
19665      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19666      */
19667     sort : function(fieldName, dir){
19668         var f = this.fields.get(fieldName);
19669         if(!dir){
19670             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19671             
19672             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19673                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19674             }else{
19675                 dir = f.sortDir;
19676             }
19677         }
19678         this.sortToggle[f.name] = dir;
19679         this.sortInfo = {field: f.name, direction: dir};
19680         if(!this.remoteSort){
19681             this.applySort();
19682             this.fireEvent("datachanged", this);
19683         }else{
19684             this.load(this.lastOptions);
19685         }
19686     },
19687
19688     /**
19689      * Calls the specified function for each of the Records in the cache.
19690      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19691      * Returning <em>false</em> aborts and exits the iteration.
19692      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19693      */
19694     each : function(fn, scope){
19695         this.data.each(fn, scope);
19696     },
19697
19698     /**
19699      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19700      * (e.g., during paging).
19701      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19702      */
19703     getModifiedRecords : function(){
19704         return this.modified;
19705     },
19706
19707     // private
19708     createFilterFn : function(property, value, anyMatch){
19709         if(!value.exec){ // not a regex
19710             value = String(value);
19711             if(value.length == 0){
19712                 return false;
19713             }
19714             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19715         }
19716         return function(r){
19717             return value.test(r.data[property]);
19718         };
19719     },
19720
19721     /**
19722      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19723      * @param {String} property A field on your records
19724      * @param {Number} start The record index to start at (defaults to 0)
19725      * @param {Number} end The last record index to include (defaults to length - 1)
19726      * @return {Number} The sum
19727      */
19728     sum : function(property, start, end){
19729         var rs = this.data.items, v = 0;
19730         start = start || 0;
19731         end = (end || end === 0) ? end : rs.length-1;
19732
19733         for(var i = start; i <= end; i++){
19734             v += (rs[i].data[property] || 0);
19735         }
19736         return v;
19737     },
19738
19739     /**
19740      * Filter the records by a specified property.
19741      * @param {String} field A field on your records
19742      * @param {String/RegExp} value Either a string that the field
19743      * should start with or a RegExp to test against the field
19744      * @param {Boolean} anyMatch True to match any part not just the beginning
19745      */
19746     filter : function(property, value, anyMatch){
19747         var fn = this.createFilterFn(property, value, anyMatch);
19748         return fn ? this.filterBy(fn) : this.clearFilter();
19749     },
19750
19751     /**
19752      * Filter by a function. The specified function will be called with each
19753      * record in this data source. If the function returns true the record is included,
19754      * otherwise it is filtered.
19755      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19756      * @param {Object} scope (optional) The scope of the function (defaults to this)
19757      */
19758     filterBy : function(fn, scope){
19759         this.snapshot = this.snapshot || this.data;
19760         this.data = this.queryBy(fn, scope||this);
19761         this.fireEvent("datachanged", this);
19762     },
19763
19764     /**
19765      * Query the records by a specified property.
19766      * @param {String} field A field on your records
19767      * @param {String/RegExp} value Either a string that the field
19768      * should start with or a RegExp to test against the field
19769      * @param {Boolean} anyMatch True to match any part not just the beginning
19770      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19771      */
19772     query : function(property, value, anyMatch){
19773         var fn = this.createFilterFn(property, value, anyMatch);
19774         return fn ? this.queryBy(fn) : this.data.clone();
19775     },
19776
19777     /**
19778      * Query by a function. The specified function will be called with each
19779      * record in this data source. If the function returns true the record is included
19780      * in the results.
19781      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19782      * @param {Object} scope (optional) The scope of the function (defaults to this)
19783       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19784      **/
19785     queryBy : function(fn, scope){
19786         var data = this.snapshot || this.data;
19787         return data.filterBy(fn, scope||this);
19788     },
19789
19790     /**
19791      * Collects unique values for a particular dataIndex from this store.
19792      * @param {String} dataIndex The property to collect
19793      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19794      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19795      * @return {Array} An array of the unique values
19796      **/
19797     collect : function(dataIndex, allowNull, bypassFilter){
19798         var d = (bypassFilter === true && this.snapshot) ?
19799                 this.snapshot.items : this.data.items;
19800         var v, sv, r = [], l = {};
19801         for(var i = 0, len = d.length; i < len; i++){
19802             v = d[i].data[dataIndex];
19803             sv = String(v);
19804             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19805                 l[sv] = true;
19806                 r[r.length] = v;
19807             }
19808         }
19809         return r;
19810     },
19811
19812     /**
19813      * Revert to a view of the Record cache with no filtering applied.
19814      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19815      */
19816     clearFilter : function(suppressEvent){
19817         if(this.snapshot && this.snapshot != this.data){
19818             this.data = this.snapshot;
19819             delete this.snapshot;
19820             if(suppressEvent !== true){
19821                 this.fireEvent("datachanged", this);
19822             }
19823         }
19824     },
19825
19826     // private
19827     afterEdit : function(record){
19828         if(this.modified.indexOf(record) == -1){
19829             this.modified.push(record);
19830         }
19831         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19832     },
19833     
19834     // private
19835     afterReject : function(record){
19836         this.modified.remove(record);
19837         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19838     },
19839
19840     // private
19841     afterCommit : function(record){
19842         this.modified.remove(record);
19843         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19844     },
19845
19846     /**
19847      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19848      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19849      */
19850     commitChanges : function(){
19851         var m = this.modified.slice(0);
19852         this.modified = [];
19853         for(var i = 0, len = m.length; i < len; i++){
19854             m[i].commit();
19855         }
19856     },
19857
19858     /**
19859      * Cancel outstanding changes on all changed records.
19860      */
19861     rejectChanges : function(){
19862         var m = this.modified.slice(0);
19863         this.modified = [];
19864         for(var i = 0, len = m.length; i < len; i++){
19865             m[i].reject();
19866         }
19867     },
19868
19869     onMetaChange : function(meta, rtype, o){
19870         this.recordType = rtype;
19871         this.fields = rtype.prototype.fields;
19872         delete this.snapshot;
19873         this.sortInfo = meta.sortInfo || this.sortInfo;
19874         this.modified = [];
19875         this.fireEvent('metachange', this, this.reader.meta);
19876     }
19877 });/*
19878  * Based on:
19879  * Ext JS Library 1.1.1
19880  * Copyright(c) 2006-2007, Ext JS, LLC.
19881  *
19882  * Originally Released Under LGPL - original licence link has changed is not relivant.
19883  *
19884  * Fork - LGPL
19885  * <script type="text/javascript">
19886  */
19887
19888 /**
19889  * @class Roo.data.SimpleStore
19890  * @extends Roo.data.Store
19891  * Small helper class to make creating Stores from Array data easier.
19892  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19893  * @cfg {Array} fields An array of field definition objects, or field name strings.
19894  * @cfg {Array} data The multi-dimensional array of data
19895  * @constructor
19896  * @param {Object} config
19897  */
19898 Roo.data.SimpleStore = function(config){
19899     Roo.data.SimpleStore.superclass.constructor.call(this, {
19900         isLocal : true,
19901         reader: new Roo.data.ArrayReader({
19902                 id: config.id
19903             },
19904             Roo.data.Record.create(config.fields)
19905         ),
19906         proxy : new Roo.data.MemoryProxy(config.data)
19907     });
19908     this.load();
19909 };
19910 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19911  * Based on:
19912  * Ext JS Library 1.1.1
19913  * Copyright(c) 2006-2007, Ext JS, LLC.
19914  *
19915  * Originally Released Under LGPL - original licence link has changed is not relivant.
19916  *
19917  * Fork - LGPL
19918  * <script type="text/javascript">
19919  */
19920
19921 /**
19922 /**
19923  * @extends Roo.data.Store
19924  * @class Roo.data.JsonStore
19925  * Small helper class to make creating Stores for JSON data easier. <br/>
19926 <pre><code>
19927 var store = new Roo.data.JsonStore({
19928     url: 'get-images.php',
19929     root: 'images',
19930     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19931 });
19932 </code></pre>
19933  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19934  * JsonReader and HttpProxy (unless inline data is provided).</b>
19935  * @cfg {Array} fields An array of field definition objects, or field name strings.
19936  * @constructor
19937  * @param {Object} config
19938  */
19939 Roo.data.JsonStore = function(c){
19940     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19941         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19942         reader: new Roo.data.JsonReader(c, c.fields)
19943     }));
19944 };
19945 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19946  * Based on:
19947  * Ext JS Library 1.1.1
19948  * Copyright(c) 2006-2007, Ext JS, LLC.
19949  *
19950  * Originally Released Under LGPL - original licence link has changed is not relivant.
19951  *
19952  * Fork - LGPL
19953  * <script type="text/javascript">
19954  */
19955
19956  
19957 Roo.data.Field = function(config){
19958     if(typeof config == "string"){
19959         config = {name: config};
19960     }
19961     Roo.apply(this, config);
19962     
19963     if(!this.type){
19964         this.type = "auto";
19965     }
19966     
19967     var st = Roo.data.SortTypes;
19968     // named sortTypes are supported, here we look them up
19969     if(typeof this.sortType == "string"){
19970         this.sortType = st[this.sortType];
19971     }
19972     
19973     // set default sortType for strings and dates
19974     if(!this.sortType){
19975         switch(this.type){
19976             case "string":
19977                 this.sortType = st.asUCString;
19978                 break;
19979             case "date":
19980                 this.sortType = st.asDate;
19981                 break;
19982             default:
19983                 this.sortType = st.none;
19984         }
19985     }
19986
19987     // define once
19988     var stripRe = /[\$,%]/g;
19989
19990     // prebuilt conversion function for this field, instead of
19991     // switching every time we're reading a value
19992     if(!this.convert){
19993         var cv, dateFormat = this.dateFormat;
19994         switch(this.type){
19995             case "":
19996             case "auto":
19997             case undefined:
19998                 cv = function(v){ return v; };
19999                 break;
20000             case "string":
20001                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
20002                 break;
20003             case "int":
20004                 cv = function(v){
20005                     return v !== undefined && v !== null && v !== '' ?
20006                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20007                     };
20008                 break;
20009             case "float":
20010                 cv = function(v){
20011                     return v !== undefined && v !== null && v !== '' ?
20012                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20013                     };
20014                 break;
20015             case "bool":
20016             case "boolean":
20017                 cv = function(v){ return v === true || v === "true" || v == 1; };
20018                 break;
20019             case "date":
20020                 cv = function(v){
20021                     if(!v){
20022                         return '';
20023                     }
20024                     if(v instanceof Date){
20025                         return v;
20026                     }
20027                     if(dateFormat){
20028                         if(dateFormat == "timestamp"){
20029                             return new Date(v*1000);
20030                         }
20031                         return Date.parseDate(v, dateFormat);
20032                     }
20033                     var parsed = Date.parse(v);
20034                     return parsed ? new Date(parsed) : null;
20035                 };
20036              break;
20037             
20038         }
20039         this.convert = cv;
20040     }
20041 };
20042
20043 Roo.data.Field.prototype = {
20044     dateFormat: null,
20045     defaultValue: "",
20046     mapping: null,
20047     sortType : null,
20048     sortDir : "ASC"
20049 };/*
20050  * Based on:
20051  * Ext JS Library 1.1.1
20052  * Copyright(c) 2006-2007, Ext JS, LLC.
20053  *
20054  * Originally Released Under LGPL - original licence link has changed is not relivant.
20055  *
20056  * Fork - LGPL
20057  * <script type="text/javascript">
20058  */
20059  
20060 // Base class for reading structured data from a data source.  This class is intended to be
20061 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20062
20063 /**
20064  * @class Roo.data.DataReader
20065  * Base class for reading structured data from a data source.  This class is intended to be
20066  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20067  */
20068
20069 Roo.data.DataReader = function(meta, recordType){
20070     
20071     this.meta = meta;
20072     
20073     this.recordType = recordType instanceof Array ? 
20074         Roo.data.Record.create(recordType) : recordType;
20075 };
20076
20077 Roo.data.DataReader.prototype = {
20078      /**
20079      * Create an empty record
20080      * @param {Object} data (optional) - overlay some values
20081      * @return {Roo.data.Record} record created.
20082      */
20083     newRow :  function(d) {
20084         var da =  {};
20085         this.recordType.prototype.fields.each(function(c) {
20086             switch( c.type) {
20087                 case 'int' : da[c.name] = 0; break;
20088                 case 'date' : da[c.name] = new Date(); break;
20089                 case 'float' : da[c.name] = 0.0; break;
20090                 case 'boolean' : da[c.name] = false; break;
20091                 default : da[c.name] = ""; break;
20092             }
20093             
20094         });
20095         return new this.recordType(Roo.apply(da, d));
20096     }
20097     
20098 };/*
20099  * Based on:
20100  * Ext JS Library 1.1.1
20101  * Copyright(c) 2006-2007, Ext JS, LLC.
20102  *
20103  * Originally Released Under LGPL - original licence link has changed is not relivant.
20104  *
20105  * Fork - LGPL
20106  * <script type="text/javascript">
20107  */
20108
20109 /**
20110  * @class Roo.data.DataProxy
20111  * @extends Roo.data.Observable
20112  * This class is an abstract base class for implementations which provide retrieval of
20113  * unformatted data objects.<br>
20114  * <p>
20115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20116  * (of the appropriate type which knows how to parse the data object) to provide a block of
20117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20118  * <p>
20119  * Custom implementations must implement the load method as described in
20120  * {@link Roo.data.HttpProxy#load}.
20121  */
20122 Roo.data.DataProxy = function(){
20123     this.addEvents({
20124         /**
20125          * @event beforeload
20126          * Fires before a network request is made to retrieve a data object.
20127          * @param {Object} This DataProxy object.
20128          * @param {Object} params The params parameter to the load function.
20129          */
20130         beforeload : true,
20131         /**
20132          * @event load
20133          * Fires before the load method's callback is called.
20134          * @param {Object} This DataProxy object.
20135          * @param {Object} o The data object.
20136          * @param {Object} arg The callback argument object passed to the load function.
20137          */
20138         load : true,
20139         /**
20140          * @event loadexception
20141          * Fires if an Exception occurs during data retrieval.
20142          * @param {Object} This DataProxy object.
20143          * @param {Object} o The data object.
20144          * @param {Object} arg The callback argument object passed to the load function.
20145          * @param {Object} e The Exception.
20146          */
20147         loadexception : true
20148     });
20149     Roo.data.DataProxy.superclass.constructor.call(this);
20150 };
20151
20152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20153
20154     /**
20155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20156      */
20157 /*
20158  * Based on:
20159  * Ext JS Library 1.1.1
20160  * Copyright(c) 2006-2007, Ext JS, LLC.
20161  *
20162  * Originally Released Under LGPL - original licence link has changed is not relivant.
20163  *
20164  * Fork - LGPL
20165  * <script type="text/javascript">
20166  */
20167 /**
20168  * @class Roo.data.MemoryProxy
20169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20170  * to the Reader when its load method is called.
20171  * @constructor
20172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20173  */
20174 Roo.data.MemoryProxy = function(data){
20175     if (data.data) {
20176         data = data.data;
20177     }
20178     Roo.data.MemoryProxy.superclass.constructor.call(this);
20179     this.data = data;
20180 };
20181
20182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20183     /**
20184      * Load data from the requested source (in this case an in-memory
20185      * data object passed to the constructor), read the data object into
20186      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20187      * process that block using the passed callback.
20188      * @param {Object} params This parameter is not used by the MemoryProxy class.
20189      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20190      * object into a block of Roo.data.Records.
20191      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20192      * The function must be passed <ul>
20193      * <li>The Record block object</li>
20194      * <li>The "arg" argument from the load function</li>
20195      * <li>A boolean success indicator</li>
20196      * </ul>
20197      * @param {Object} scope The scope in which to call the callback
20198      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20199      */
20200     load : function(params, reader, callback, scope, arg){
20201         params = params || {};
20202         var result;
20203         try {
20204             result = reader.readRecords(this.data);
20205         }catch(e){
20206             this.fireEvent("loadexception", this, arg, null, e);
20207             callback.call(scope, null, arg, false);
20208             return;
20209         }
20210         callback.call(scope, result, arg, true);
20211     },
20212     
20213     // private
20214     update : function(params, records){
20215         
20216     }
20217 });/*
20218  * Based on:
20219  * Ext JS Library 1.1.1
20220  * Copyright(c) 2006-2007, Ext JS, LLC.
20221  *
20222  * Originally Released Under LGPL - original licence link has changed is not relivant.
20223  *
20224  * Fork - LGPL
20225  * <script type="text/javascript">
20226  */
20227 /**
20228  * @class Roo.data.HttpProxy
20229  * @extends Roo.data.DataProxy
20230  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20231  * configured to reference a certain URL.<br><br>
20232  * <p>
20233  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20234  * from which the running page was served.<br><br>
20235  * <p>
20236  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20237  * <p>
20238  * Be aware that to enable the browser to parse an XML document, the server must set
20239  * the Content-Type header in the HTTP response to "text/xml".
20240  * @constructor
20241  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20242  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20243  * will be used to make the request.
20244  */
20245 Roo.data.HttpProxy = function(conn){
20246     Roo.data.HttpProxy.superclass.constructor.call(this);
20247     // is conn a conn config or a real conn?
20248     this.conn = conn;
20249     this.useAjax = !conn || !conn.events;
20250   
20251 };
20252
20253 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20254     // thse are take from connection...
20255     
20256     /**
20257      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20258      */
20259     /**
20260      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20261      * extra parameters to each request made by this object. (defaults to undefined)
20262      */
20263     /**
20264      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20265      *  to each request made by this object. (defaults to undefined)
20266      */
20267     /**
20268      * @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)
20269      */
20270     /**
20271      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20272      */
20273      /**
20274      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20275      * @type Boolean
20276      */
20277   
20278
20279     /**
20280      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20281      * @type Boolean
20282      */
20283     /**
20284      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20285      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20286      * a finer-grained basis than the DataProxy events.
20287      */
20288     getConnection : function(){
20289         return this.useAjax ? Roo.Ajax : this.conn;
20290     },
20291
20292     /**
20293      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20294      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20295      * process that block using the passed callback.
20296      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20297      * for the request to the remote server.
20298      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20299      * object into a block of Roo.data.Records.
20300      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20301      * The function must be passed <ul>
20302      * <li>The Record block object</li>
20303      * <li>The "arg" argument from the load function</li>
20304      * <li>A boolean success indicator</li>
20305      * </ul>
20306      * @param {Object} scope The scope in which to call the callback
20307      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20308      */
20309     load : function(params, reader, callback, scope, arg){
20310         if(this.fireEvent("beforeload", this, params) !== false){
20311             var  o = {
20312                 params : params || {},
20313                 request: {
20314                     callback : callback,
20315                     scope : scope,
20316                     arg : arg
20317                 },
20318                 reader: reader,
20319                 callback : this.loadResponse,
20320                 scope: this
20321             };
20322             if(this.useAjax){
20323                 Roo.applyIf(o, this.conn);
20324                 if(this.activeRequest){
20325                     Roo.Ajax.abort(this.activeRequest);
20326                 }
20327                 this.activeRequest = Roo.Ajax.request(o);
20328             }else{
20329                 this.conn.request(o);
20330             }
20331         }else{
20332             callback.call(scope||this, null, arg, false);
20333         }
20334     },
20335
20336     // private
20337     loadResponse : function(o, success, response){
20338         delete this.activeRequest;
20339         if(!success){
20340             this.fireEvent("loadexception", this, o, response);
20341             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20342             return;
20343         }
20344         var result;
20345         try {
20346             result = o.reader.read(response);
20347         }catch(e){
20348             this.fireEvent("loadexception", this, o, response, e);
20349             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20350             return;
20351         }
20352         
20353         this.fireEvent("load", this, o, o.request.arg);
20354         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20355     },
20356
20357     // private
20358     update : function(dataSet){
20359
20360     },
20361
20362     // private
20363     updateResponse : function(dataSet){
20364
20365     }
20366 });/*
20367  * Based on:
20368  * Ext JS Library 1.1.1
20369  * Copyright(c) 2006-2007, Ext JS, LLC.
20370  *
20371  * Originally Released Under LGPL - original licence link has changed is not relivant.
20372  *
20373  * Fork - LGPL
20374  * <script type="text/javascript">
20375  */
20376
20377 /**
20378  * @class Roo.data.ScriptTagProxy
20379  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20380  * other than the originating domain of the running page.<br><br>
20381  * <p>
20382  * <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
20383  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20384  * <p>
20385  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20386  * source code that is used as the source inside a &lt;script> tag.<br><br>
20387  * <p>
20388  * In order for the browser to process the returned data, the server must wrap the data object
20389  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20390  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20391  * depending on whether the callback name was passed:
20392  * <p>
20393  * <pre><code>
20394 boolean scriptTag = false;
20395 String cb = request.getParameter("callback");
20396 if (cb != null) {
20397     scriptTag = true;
20398     response.setContentType("text/javascript");
20399 } else {
20400     response.setContentType("application/x-json");
20401 }
20402 Writer out = response.getWriter();
20403 if (scriptTag) {
20404     out.write(cb + "(");
20405 }
20406 out.print(dataBlock.toJsonString());
20407 if (scriptTag) {
20408     out.write(");");
20409 }
20410 </pre></code>
20411  *
20412  * @constructor
20413  * @param {Object} config A configuration object.
20414  */
20415 Roo.data.ScriptTagProxy = function(config){
20416     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20417     Roo.apply(this, config);
20418     this.head = document.getElementsByTagName("head")[0];
20419 };
20420
20421 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20422
20423 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20424     /**
20425      * @cfg {String} url The URL from which to request the data object.
20426      */
20427     /**
20428      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20429      */
20430     timeout : 30000,
20431     /**
20432      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20433      * the server the name of the callback function set up by the load call to process the returned data object.
20434      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20435      * javascript output which calls this named function passing the data object as its only parameter.
20436      */
20437     callbackParam : "callback",
20438     /**
20439      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20440      * name to the request.
20441      */
20442     nocache : true,
20443
20444     /**
20445      * Load data from the configured URL, read the data object into
20446      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20447      * process that block using the passed callback.
20448      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20449      * for the request to the remote server.
20450      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20451      * object into a block of Roo.data.Records.
20452      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20453      * The function must be passed <ul>
20454      * <li>The Record block object</li>
20455      * <li>The "arg" argument from the load function</li>
20456      * <li>A boolean success indicator</li>
20457      * </ul>
20458      * @param {Object} scope The scope in which to call the callback
20459      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20460      */
20461     load : function(params, reader, callback, scope, arg){
20462         if(this.fireEvent("beforeload", this, params) !== false){
20463
20464             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20465
20466             var url = this.url;
20467             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20468             if(this.nocache){
20469                 url += "&_dc=" + (new Date().getTime());
20470             }
20471             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20472             var trans = {
20473                 id : transId,
20474                 cb : "stcCallback"+transId,
20475                 scriptId : "stcScript"+transId,
20476                 params : params,
20477                 arg : arg,
20478                 url : url,
20479                 callback : callback,
20480                 scope : scope,
20481                 reader : reader
20482             };
20483             var conn = this;
20484
20485             window[trans.cb] = function(o){
20486                 conn.handleResponse(o, trans);
20487             };
20488
20489             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20490
20491             if(this.autoAbort !== false){
20492                 this.abort();
20493             }
20494
20495             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20496
20497             var script = document.createElement("script");
20498             script.setAttribute("src", url);
20499             script.setAttribute("type", "text/javascript");
20500             script.setAttribute("id", trans.scriptId);
20501             this.head.appendChild(script);
20502
20503             this.trans = trans;
20504         }else{
20505             callback.call(scope||this, null, arg, false);
20506         }
20507     },
20508
20509     // private
20510     isLoading : function(){
20511         return this.trans ? true : false;
20512     },
20513
20514     /**
20515      * Abort the current server request.
20516      */
20517     abort : function(){
20518         if(this.isLoading()){
20519             this.destroyTrans(this.trans);
20520         }
20521     },
20522
20523     // private
20524     destroyTrans : function(trans, isLoaded){
20525         this.head.removeChild(document.getElementById(trans.scriptId));
20526         clearTimeout(trans.timeoutId);
20527         if(isLoaded){
20528             window[trans.cb] = undefined;
20529             try{
20530                 delete window[trans.cb];
20531             }catch(e){}
20532         }else{
20533             // if hasn't been loaded, wait for load to remove it to prevent script error
20534             window[trans.cb] = function(){
20535                 window[trans.cb] = undefined;
20536                 try{
20537                     delete window[trans.cb];
20538                 }catch(e){}
20539             };
20540         }
20541     },
20542
20543     // private
20544     handleResponse : function(o, trans){
20545         this.trans = false;
20546         this.destroyTrans(trans, true);
20547         var result;
20548         try {
20549             result = trans.reader.readRecords(o);
20550         }catch(e){
20551             this.fireEvent("loadexception", this, o, trans.arg, e);
20552             trans.callback.call(trans.scope||window, null, trans.arg, false);
20553             return;
20554         }
20555         this.fireEvent("load", this, o, trans.arg);
20556         trans.callback.call(trans.scope||window, result, trans.arg, true);
20557     },
20558
20559     // private
20560     handleFailure : function(trans){
20561         this.trans = false;
20562         this.destroyTrans(trans, false);
20563         this.fireEvent("loadexception", this, null, trans.arg);
20564         trans.callback.call(trans.scope||window, null, trans.arg, false);
20565     }
20566 });/*
20567  * Based on:
20568  * Ext JS Library 1.1.1
20569  * Copyright(c) 2006-2007, Ext JS, LLC.
20570  *
20571  * Originally Released Under LGPL - original licence link has changed is not relivant.
20572  *
20573  * Fork - LGPL
20574  * <script type="text/javascript">
20575  */
20576
20577 /**
20578  * @class Roo.data.JsonReader
20579  * @extends Roo.data.DataReader
20580  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20581  * based on mappings in a provided Roo.data.Record constructor.
20582  * 
20583  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20584  * in the reply previously. 
20585  * 
20586  * <p>
20587  * Example code:
20588  * <pre><code>
20589 var RecordDef = Roo.data.Record.create([
20590     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20591     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20592 ]);
20593 var myReader = new Roo.data.JsonReader({
20594     totalProperty: "results",    // The property which contains the total dataset size (optional)
20595     root: "rows",                // The property which contains an Array of row objects
20596     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20597 }, RecordDef);
20598 </code></pre>
20599  * <p>
20600  * This would consume a JSON file like this:
20601  * <pre><code>
20602 { 'results': 2, 'rows': [
20603     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20604     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20605 }
20606 </code></pre>
20607  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20608  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20609  * paged from the remote server.
20610  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20611  * @cfg {String} root name of the property which contains the Array of row objects.
20612  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20613  * @constructor
20614  * Create a new JsonReader
20615  * @param {Object} meta Metadata configuration options
20616  * @param {Object} recordType Either an Array of field definition objects,
20617  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20618  */
20619 Roo.data.JsonReader = function(meta, recordType){
20620     
20621     meta = meta || {};
20622     // set some defaults:
20623     Roo.applyIf(meta, {
20624         totalProperty: 'total',
20625         successProperty : 'success',
20626         root : 'data',
20627         id : 'id'
20628     });
20629     
20630     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20631 };
20632 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20633     
20634     /**
20635      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20636      * Used by Store query builder to append _requestMeta to params.
20637      * 
20638      */
20639     metaFromRemote : false,
20640     /**
20641      * This method is only used by a DataProxy which has retrieved data from a remote server.
20642      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20643      * @return {Object} data A data block which is used by an Roo.data.Store object as
20644      * a cache of Roo.data.Records.
20645      */
20646     read : function(response){
20647         var json = response.responseText;
20648        
20649         var o = /* eval:var:o */ eval("("+json+")");
20650         if(!o) {
20651             throw {message: "JsonReader.read: Json object not found"};
20652         }
20653         
20654         if(o.metaData){
20655             
20656             delete this.ef;
20657             this.metaFromRemote = true;
20658             this.meta = o.metaData;
20659             this.recordType = Roo.data.Record.create(o.metaData.fields);
20660             this.onMetaChange(this.meta, this.recordType, o);
20661         }
20662         return this.readRecords(o);
20663     },
20664
20665     // private function a store will implement
20666     onMetaChange : function(meta, recordType, o){
20667
20668     },
20669
20670     /**
20671          * @ignore
20672          */
20673     simpleAccess: function(obj, subsc) {
20674         return obj[subsc];
20675     },
20676
20677         /**
20678          * @ignore
20679          */
20680     getJsonAccessor: function(){
20681         var re = /[\[\.]/;
20682         return function(expr) {
20683             try {
20684                 return(re.test(expr))
20685                     ? new Function("obj", "return obj." + expr)
20686                     : function(obj){
20687                         return obj[expr];
20688                     };
20689             } catch(e){}
20690             return Roo.emptyFn;
20691         };
20692     }(),
20693
20694     /**
20695      * Create a data block containing Roo.data.Records from an XML document.
20696      * @param {Object} o An object which contains an Array of row objects in the property specified
20697      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20698      * which contains the total size of the dataset.
20699      * @return {Object} data A data block which is used by an Roo.data.Store object as
20700      * a cache of Roo.data.Records.
20701      */
20702     readRecords : function(o){
20703         /**
20704          * After any data loads, the raw JSON data is available for further custom processing.
20705          * @type Object
20706          */
20707         this.jsonData = o;
20708         var s = this.meta, Record = this.recordType,
20709             f = Record.prototype.fields, fi = f.items, fl = f.length;
20710
20711 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20712         if (!this.ef) {
20713             if(s.totalProperty) {
20714                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20715                 }
20716                 if(s.successProperty) {
20717                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20718                 }
20719                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20720                 if (s.id) {
20721                         var g = this.getJsonAccessor(s.id);
20722                         this.getId = function(rec) {
20723                                 var r = g(rec);
20724                                 return (r === undefined || r === "") ? null : r;
20725                         };
20726                 } else {
20727                         this.getId = function(){return null;};
20728                 }
20729             this.ef = [];
20730             for(var jj = 0; jj < fl; jj++){
20731                 f = fi[jj];
20732                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20733                 this.ef[jj] = this.getJsonAccessor(map);
20734             }
20735         }
20736
20737         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20738         if(s.totalProperty){
20739             var vt = parseInt(this.getTotal(o), 10);
20740             if(!isNaN(vt)){
20741                 totalRecords = vt;
20742             }
20743         }
20744         if(s.successProperty){
20745             var vs = this.getSuccess(o);
20746             if(vs === false || vs === 'false'){
20747                 success = false;
20748             }
20749         }
20750         var records = [];
20751             for(var i = 0; i < c; i++){
20752                     var n = root[i];
20753                 var values = {};
20754                 var id = this.getId(n);
20755                 for(var j = 0; j < fl; j++){
20756                     f = fi[j];
20757                 var v = this.ef[j](n);
20758                 if (!f.convert) {
20759                     Roo.log('missing convert for ' + f.name);
20760                     Roo.log(f);
20761                     continue;
20762                 }
20763                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20764                 }
20765                 var record = new Record(values, id);
20766                 record.json = n;
20767                 records[i] = record;
20768             }
20769             return {
20770                 success : success,
20771                 records : records,
20772                 totalRecords : totalRecords
20773             };
20774     }
20775 });/*
20776  * Based on:
20777  * Ext JS Library 1.1.1
20778  * Copyright(c) 2006-2007, Ext JS, LLC.
20779  *
20780  * Originally Released Under LGPL - original licence link has changed is not relivant.
20781  *
20782  * Fork - LGPL
20783  * <script type="text/javascript">
20784  */
20785
20786 /**
20787  * @class Roo.data.XmlReader
20788  * @extends Roo.data.DataReader
20789  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20790  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20791  * <p>
20792  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20793  * header in the HTTP response must be set to "text/xml".</em>
20794  * <p>
20795  * Example code:
20796  * <pre><code>
20797 var RecordDef = Roo.data.Record.create([
20798    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20799    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20800 ]);
20801 var myReader = new Roo.data.XmlReader({
20802    totalRecords: "results", // The element which contains the total dataset size (optional)
20803    record: "row",           // The repeated element which contains row information
20804    id: "id"                 // The element within the row that provides an ID for the record (optional)
20805 }, RecordDef);
20806 </code></pre>
20807  * <p>
20808  * This would consume an XML file like this:
20809  * <pre><code>
20810 &lt;?xml?>
20811 &lt;dataset>
20812  &lt;results>2&lt;/results>
20813  &lt;row>
20814    &lt;id>1&lt;/id>
20815    &lt;name>Bill&lt;/name>
20816    &lt;occupation>Gardener&lt;/occupation>
20817  &lt;/row>
20818  &lt;row>
20819    &lt;id>2&lt;/id>
20820    &lt;name>Ben&lt;/name>
20821    &lt;occupation>Horticulturalist&lt;/occupation>
20822  &lt;/row>
20823 &lt;/dataset>
20824 </code></pre>
20825  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20826  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20827  * paged from the remote server.
20828  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20829  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20830  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20831  * a record identifier value.
20832  * @constructor
20833  * Create a new XmlReader
20834  * @param {Object} meta Metadata configuration options
20835  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20836  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20837  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20838  */
20839 Roo.data.XmlReader = function(meta, recordType){
20840     meta = meta || {};
20841     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20842 };
20843 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20844     /**
20845      * This method is only used by a DataProxy which has retrieved data from a remote server.
20846          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20847          * to contain a method called 'responseXML' that returns an XML document object.
20848      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20849      * a cache of Roo.data.Records.
20850      */
20851     read : function(response){
20852         var doc = response.responseXML;
20853         if(!doc) {
20854             throw {message: "XmlReader.read: XML Document not available"};
20855         }
20856         return this.readRecords(doc);
20857     },
20858
20859     /**
20860      * Create a data block containing Roo.data.Records from an XML document.
20861          * @param {Object} doc A parsed XML document.
20862      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20863      * a cache of Roo.data.Records.
20864      */
20865     readRecords : function(doc){
20866         /**
20867          * After any data loads/reads, the raw XML Document is available for further custom processing.
20868          * @type XMLDocument
20869          */
20870         this.xmlData = doc;
20871         var root = doc.documentElement || doc;
20872         var q = Roo.DomQuery;
20873         var recordType = this.recordType, fields = recordType.prototype.fields;
20874         var sid = this.meta.id;
20875         var totalRecords = 0, success = true;
20876         if(this.meta.totalRecords){
20877             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20878         }
20879         
20880         if(this.meta.success){
20881             var sv = q.selectValue(this.meta.success, root, true);
20882             success = sv !== false && sv !== 'false';
20883         }
20884         var records = [];
20885         var ns = q.select(this.meta.record, root);
20886         for(var i = 0, len = ns.length; i < len; i++) {
20887                 var n = ns[i];
20888                 var values = {};
20889                 var id = sid ? q.selectValue(sid, n) : undefined;
20890                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20891                     var f = fields.items[j];
20892                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20893                     v = f.convert(v);
20894                     values[f.name] = v;
20895                 }
20896                 var record = new recordType(values, id);
20897                 record.node = n;
20898                 records[records.length] = record;
20899             }
20900
20901             return {
20902                 success : success,
20903                 records : records,
20904                 totalRecords : totalRecords || records.length
20905             };
20906     }
20907 });/*
20908  * Based on:
20909  * Ext JS Library 1.1.1
20910  * Copyright(c) 2006-2007, Ext JS, LLC.
20911  *
20912  * Originally Released Under LGPL - original licence link has changed is not relivant.
20913  *
20914  * Fork - LGPL
20915  * <script type="text/javascript">
20916  */
20917
20918 /**
20919  * @class Roo.data.ArrayReader
20920  * @extends Roo.data.DataReader
20921  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20922  * Each element of that Array represents a row of data fields. The
20923  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20924  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20925  * <p>
20926  * Example code:.
20927  * <pre><code>
20928 var RecordDef = Roo.data.Record.create([
20929     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20930     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20931 ]);
20932 var myReader = new Roo.data.ArrayReader({
20933     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20934 }, RecordDef);
20935 </code></pre>
20936  * <p>
20937  * This would consume an Array like this:
20938  * <pre><code>
20939 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20940   </code></pre>
20941  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20942  * @constructor
20943  * Create a new JsonReader
20944  * @param {Object} meta Metadata configuration options.
20945  * @param {Object} recordType Either an Array of field definition objects
20946  * as specified to {@link Roo.data.Record#create},
20947  * or an {@link Roo.data.Record} object
20948  * created using {@link Roo.data.Record#create}.
20949  */
20950 Roo.data.ArrayReader = function(meta, recordType){
20951     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20952 };
20953
20954 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20955     /**
20956      * Create a data block containing Roo.data.Records from an XML document.
20957      * @param {Object} o An Array of row objects which represents the dataset.
20958      * @return {Object} data A data block which is used by an Roo.data.Store object as
20959      * a cache of Roo.data.Records.
20960      */
20961     readRecords : function(o){
20962         var sid = this.meta ? this.meta.id : null;
20963         var recordType = this.recordType, fields = recordType.prototype.fields;
20964         var records = [];
20965         var root = o;
20966             for(var i = 0; i < root.length; i++){
20967                     var n = root[i];
20968                 var values = {};
20969                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20970                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20971                 var f = fields.items[j];
20972                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20973                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20974                 v = f.convert(v);
20975                 values[f.name] = v;
20976             }
20977                 var record = new recordType(values, id);
20978                 record.json = n;
20979                 records[records.length] = record;
20980             }
20981             return {
20982                 records : records,
20983                 totalRecords : records.length
20984             };
20985     }
20986 });/*
20987  * Based on:
20988  * Ext JS Library 1.1.1
20989  * Copyright(c) 2006-2007, Ext JS, LLC.
20990  *
20991  * Originally Released Under LGPL - original licence link has changed is not relivant.
20992  *
20993  * Fork - LGPL
20994  * <script type="text/javascript">
20995  */
20996
20997
20998 /**
20999  * @class Roo.data.Tree
21000  * @extends Roo.util.Observable
21001  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
21002  * in the tree have most standard DOM functionality.
21003  * @constructor
21004  * @param {Node} root (optional) The root node
21005  */
21006 Roo.data.Tree = function(root){
21007    this.nodeHash = {};
21008    /**
21009     * The root node for this tree
21010     * @type Node
21011     */
21012    this.root = null;
21013    if(root){
21014        this.setRootNode(root);
21015    }
21016    this.addEvents({
21017        /**
21018         * @event append
21019         * Fires when a new child node is appended to a node in this tree.
21020         * @param {Tree} tree The owner tree
21021         * @param {Node} parent The parent node
21022         * @param {Node} node The newly appended node
21023         * @param {Number} index The index of the newly appended node
21024         */
21025        "append" : true,
21026        /**
21027         * @event remove
21028         * Fires when a child node is removed from a node in this tree.
21029         * @param {Tree} tree The owner tree
21030         * @param {Node} parent The parent node
21031         * @param {Node} node The child node removed
21032         */
21033        "remove" : true,
21034        /**
21035         * @event move
21036         * Fires when a node is moved to a new location in the tree
21037         * @param {Tree} tree The owner tree
21038         * @param {Node} node The node moved
21039         * @param {Node} oldParent The old parent of this node
21040         * @param {Node} newParent The new parent of this node
21041         * @param {Number} index The index it was moved to
21042         */
21043        "move" : true,
21044        /**
21045         * @event insert
21046         * Fires when a new child node is inserted in a node in this tree.
21047         * @param {Tree} tree The owner tree
21048         * @param {Node} parent The parent node
21049         * @param {Node} node The child node inserted
21050         * @param {Node} refNode The child node the node was inserted before
21051         */
21052        "insert" : true,
21053        /**
21054         * @event beforeappend
21055         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21056         * @param {Tree} tree The owner tree
21057         * @param {Node} parent The parent node
21058         * @param {Node} node The child node to be appended
21059         */
21060        "beforeappend" : true,
21061        /**
21062         * @event beforeremove
21063         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21064         * @param {Tree} tree The owner tree
21065         * @param {Node} parent The parent node
21066         * @param {Node} node The child node to be removed
21067         */
21068        "beforeremove" : true,
21069        /**
21070         * @event beforemove
21071         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21072         * @param {Tree} tree The owner tree
21073         * @param {Node} node The node being moved
21074         * @param {Node} oldParent The parent of the node
21075         * @param {Node} newParent The new parent the node is moving to
21076         * @param {Number} index The index it is being moved to
21077         */
21078        "beforemove" : true,
21079        /**
21080         * @event beforeinsert
21081         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21082         * @param {Tree} tree The owner tree
21083         * @param {Node} parent The parent node
21084         * @param {Node} node The child node to be inserted
21085         * @param {Node} refNode The child node the node is being inserted before
21086         */
21087        "beforeinsert" : true
21088    });
21089
21090     Roo.data.Tree.superclass.constructor.call(this);
21091 };
21092
21093 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21094     pathSeparator: "/",
21095
21096     proxyNodeEvent : function(){
21097         return this.fireEvent.apply(this, arguments);
21098     },
21099
21100     /**
21101      * Returns the root node for this tree.
21102      * @return {Node}
21103      */
21104     getRootNode : function(){
21105         return this.root;
21106     },
21107
21108     /**
21109      * Sets the root node for this tree.
21110      * @param {Node} node
21111      * @return {Node}
21112      */
21113     setRootNode : function(node){
21114         this.root = node;
21115         node.ownerTree = this;
21116         node.isRoot = true;
21117         this.registerNode(node);
21118         return node;
21119     },
21120
21121     /**
21122      * Gets a node in this tree by its id.
21123      * @param {String} id
21124      * @return {Node}
21125      */
21126     getNodeById : function(id){
21127         return this.nodeHash[id];
21128     },
21129
21130     registerNode : function(node){
21131         this.nodeHash[node.id] = node;
21132     },
21133
21134     unregisterNode : function(node){
21135         delete this.nodeHash[node.id];
21136     },
21137
21138     toString : function(){
21139         return "[Tree"+(this.id?" "+this.id:"")+"]";
21140     }
21141 });
21142
21143 /**
21144  * @class Roo.data.Node
21145  * @extends Roo.util.Observable
21146  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21147  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21148  * @constructor
21149  * @param {Object} attributes The attributes/config for the node
21150  */
21151 Roo.data.Node = function(attributes){
21152     /**
21153      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21154      * @type {Object}
21155      */
21156     this.attributes = attributes || {};
21157     this.leaf = this.attributes.leaf;
21158     /**
21159      * The node id. @type String
21160      */
21161     this.id = this.attributes.id;
21162     if(!this.id){
21163         this.id = Roo.id(null, "ynode-");
21164         this.attributes.id = this.id;
21165     }
21166     /**
21167      * All child nodes of this node. @type Array
21168      */
21169     this.childNodes = [];
21170     if(!this.childNodes.indexOf){ // indexOf is a must
21171         this.childNodes.indexOf = function(o){
21172             for(var i = 0, len = this.length; i < len; i++){
21173                 if(this[i] == o) {
21174                     return i;
21175                 }
21176             }
21177             return -1;
21178         };
21179     }
21180     /**
21181      * The parent node for this node. @type Node
21182      */
21183     this.parentNode = null;
21184     /**
21185      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21186      */
21187     this.firstChild = null;
21188     /**
21189      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21190      */
21191     this.lastChild = null;
21192     /**
21193      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21194      */
21195     this.previousSibling = null;
21196     /**
21197      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21198      */
21199     this.nextSibling = null;
21200
21201     this.addEvents({
21202        /**
21203         * @event append
21204         * Fires when a new child node is appended
21205         * @param {Tree} tree The owner tree
21206         * @param {Node} this This node
21207         * @param {Node} node The newly appended node
21208         * @param {Number} index The index of the newly appended node
21209         */
21210        "append" : true,
21211        /**
21212         * @event remove
21213         * Fires when a child node is removed
21214         * @param {Tree} tree The owner tree
21215         * @param {Node} this This node
21216         * @param {Node} node The removed node
21217         */
21218        "remove" : true,
21219        /**
21220         * @event move
21221         * Fires when this node is moved to a new location in the tree
21222         * @param {Tree} tree The owner tree
21223         * @param {Node} this This node
21224         * @param {Node} oldParent The old parent of this node
21225         * @param {Node} newParent The new parent of this node
21226         * @param {Number} index The index it was moved to
21227         */
21228        "move" : true,
21229        /**
21230         * @event insert
21231         * Fires when a new child node is inserted.
21232         * @param {Tree} tree The owner tree
21233         * @param {Node} this This node
21234         * @param {Node} node The child node inserted
21235         * @param {Node} refNode The child node the node was inserted before
21236         */
21237        "insert" : true,
21238        /**
21239         * @event beforeappend
21240         * Fires before a new child is appended, return false to cancel the append.
21241         * @param {Tree} tree The owner tree
21242         * @param {Node} this This node
21243         * @param {Node} node The child node to be appended
21244         */
21245        "beforeappend" : true,
21246        /**
21247         * @event beforeremove
21248         * Fires before a child is removed, return false to cancel the remove.
21249         * @param {Tree} tree The owner tree
21250         * @param {Node} this This node
21251         * @param {Node} node The child node to be removed
21252         */
21253        "beforeremove" : true,
21254        /**
21255         * @event beforemove
21256         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21257         * @param {Tree} tree The owner tree
21258         * @param {Node} this This node
21259         * @param {Node} oldParent The parent of this node
21260         * @param {Node} newParent The new parent this node is moving to
21261         * @param {Number} index The index it is being moved to
21262         */
21263        "beforemove" : true,
21264        /**
21265         * @event beforeinsert
21266         * Fires before a new child is inserted, return false to cancel the insert.
21267         * @param {Tree} tree The owner tree
21268         * @param {Node} this This node
21269         * @param {Node} node The child node to be inserted
21270         * @param {Node} refNode The child node the node is being inserted before
21271         */
21272        "beforeinsert" : true
21273    });
21274     this.listeners = this.attributes.listeners;
21275     Roo.data.Node.superclass.constructor.call(this);
21276 };
21277
21278 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21279     fireEvent : function(evtName){
21280         // first do standard event for this node
21281         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21282             return false;
21283         }
21284         // then bubble it up to the tree if the event wasn't cancelled
21285         var ot = this.getOwnerTree();
21286         if(ot){
21287             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21288                 return false;
21289             }
21290         }
21291         return true;
21292     },
21293
21294     /**
21295      * Returns true if this node is a leaf
21296      * @return {Boolean}
21297      */
21298     isLeaf : function(){
21299         return this.leaf === true;
21300     },
21301
21302     // private
21303     setFirstChild : function(node){
21304         this.firstChild = node;
21305     },
21306
21307     //private
21308     setLastChild : function(node){
21309         this.lastChild = node;
21310     },
21311
21312
21313     /**
21314      * Returns true if this node is the last child of its parent
21315      * @return {Boolean}
21316      */
21317     isLast : function(){
21318        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21319     },
21320
21321     /**
21322      * Returns true if this node is the first child of its parent
21323      * @return {Boolean}
21324      */
21325     isFirst : function(){
21326        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21327     },
21328
21329     hasChildNodes : function(){
21330         return !this.isLeaf() && this.childNodes.length > 0;
21331     },
21332
21333     /**
21334      * Insert node(s) as the last child node of this node.
21335      * @param {Node/Array} node The node or Array of nodes to append
21336      * @return {Node} The appended node if single append, or null if an array was passed
21337      */
21338     appendChild : function(node){
21339         var multi = false;
21340         if(node instanceof Array){
21341             multi = node;
21342         }else if(arguments.length > 1){
21343             multi = arguments;
21344         }
21345         // if passed an array or multiple args do them one by one
21346         if(multi){
21347             for(var i = 0, len = multi.length; i < len; i++) {
21348                 this.appendChild(multi[i]);
21349             }
21350         }else{
21351             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21352                 return false;
21353             }
21354             var index = this.childNodes.length;
21355             var oldParent = node.parentNode;
21356             // it's a move, make sure we move it cleanly
21357             if(oldParent){
21358                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21359                     return false;
21360                 }
21361                 oldParent.removeChild(node);
21362             }
21363             index = this.childNodes.length;
21364             if(index == 0){
21365                 this.setFirstChild(node);
21366             }
21367             this.childNodes.push(node);
21368             node.parentNode = this;
21369             var ps = this.childNodes[index-1];
21370             if(ps){
21371                 node.previousSibling = ps;
21372                 ps.nextSibling = node;
21373             }else{
21374                 node.previousSibling = null;
21375             }
21376             node.nextSibling = null;
21377             this.setLastChild(node);
21378             node.setOwnerTree(this.getOwnerTree());
21379             this.fireEvent("append", this.ownerTree, this, node, index);
21380             if(oldParent){
21381                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21382             }
21383             return node;
21384         }
21385     },
21386
21387     /**
21388      * Removes a child node from this node.
21389      * @param {Node} node The node to remove
21390      * @return {Node} The removed node
21391      */
21392     removeChild : function(node){
21393         var index = this.childNodes.indexOf(node);
21394         if(index == -1){
21395             return false;
21396         }
21397         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21398             return false;
21399         }
21400
21401         // remove it from childNodes collection
21402         this.childNodes.splice(index, 1);
21403
21404         // update siblings
21405         if(node.previousSibling){
21406             node.previousSibling.nextSibling = node.nextSibling;
21407         }
21408         if(node.nextSibling){
21409             node.nextSibling.previousSibling = node.previousSibling;
21410         }
21411
21412         // update child refs
21413         if(this.firstChild == node){
21414             this.setFirstChild(node.nextSibling);
21415         }
21416         if(this.lastChild == node){
21417             this.setLastChild(node.previousSibling);
21418         }
21419
21420         node.setOwnerTree(null);
21421         // clear any references from the node
21422         node.parentNode = null;
21423         node.previousSibling = null;
21424         node.nextSibling = null;
21425         this.fireEvent("remove", this.ownerTree, this, node);
21426         return node;
21427     },
21428
21429     /**
21430      * Inserts the first node before the second node in this nodes childNodes collection.
21431      * @param {Node} node The node to insert
21432      * @param {Node} refNode The node to insert before (if null the node is appended)
21433      * @return {Node} The inserted node
21434      */
21435     insertBefore : function(node, refNode){
21436         if(!refNode){ // like standard Dom, refNode can be null for append
21437             return this.appendChild(node);
21438         }
21439         // nothing to do
21440         if(node == refNode){
21441             return false;
21442         }
21443
21444         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21445             return false;
21446         }
21447         var index = this.childNodes.indexOf(refNode);
21448         var oldParent = node.parentNode;
21449         var refIndex = index;
21450
21451         // when moving internally, indexes will change after remove
21452         if(oldParent == this && this.childNodes.indexOf(node) < index){
21453             refIndex--;
21454         }
21455
21456         // it's a move, make sure we move it cleanly
21457         if(oldParent){
21458             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21459                 return false;
21460             }
21461             oldParent.removeChild(node);
21462         }
21463         if(refIndex == 0){
21464             this.setFirstChild(node);
21465         }
21466         this.childNodes.splice(refIndex, 0, node);
21467         node.parentNode = this;
21468         var ps = this.childNodes[refIndex-1];
21469         if(ps){
21470             node.previousSibling = ps;
21471             ps.nextSibling = node;
21472         }else{
21473             node.previousSibling = null;
21474         }
21475         node.nextSibling = refNode;
21476         refNode.previousSibling = node;
21477         node.setOwnerTree(this.getOwnerTree());
21478         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21479         if(oldParent){
21480             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21481         }
21482         return node;
21483     },
21484
21485     /**
21486      * Returns the child node at the specified index.
21487      * @param {Number} index
21488      * @return {Node}
21489      */
21490     item : function(index){
21491         return this.childNodes[index];
21492     },
21493
21494     /**
21495      * Replaces one child node in this node with another.
21496      * @param {Node} newChild The replacement node
21497      * @param {Node} oldChild The node to replace
21498      * @return {Node} The replaced node
21499      */
21500     replaceChild : function(newChild, oldChild){
21501         this.insertBefore(newChild, oldChild);
21502         this.removeChild(oldChild);
21503         return oldChild;
21504     },
21505
21506     /**
21507      * Returns the index of a child node
21508      * @param {Node} node
21509      * @return {Number} The index of the node or -1 if it was not found
21510      */
21511     indexOf : function(child){
21512         return this.childNodes.indexOf(child);
21513     },
21514
21515     /**
21516      * Returns the tree this node is in.
21517      * @return {Tree}
21518      */
21519     getOwnerTree : function(){
21520         // if it doesn't have one, look for one
21521         if(!this.ownerTree){
21522             var p = this;
21523             while(p){
21524                 if(p.ownerTree){
21525                     this.ownerTree = p.ownerTree;
21526                     break;
21527                 }
21528                 p = p.parentNode;
21529             }
21530         }
21531         return this.ownerTree;
21532     },
21533
21534     /**
21535      * Returns depth of this node (the root node has a depth of 0)
21536      * @return {Number}
21537      */
21538     getDepth : function(){
21539         var depth = 0;
21540         var p = this;
21541         while(p.parentNode){
21542             ++depth;
21543             p = p.parentNode;
21544         }
21545         return depth;
21546     },
21547
21548     // private
21549     setOwnerTree : function(tree){
21550         // if it's move, we need to update everyone
21551         if(tree != this.ownerTree){
21552             if(this.ownerTree){
21553                 this.ownerTree.unregisterNode(this);
21554             }
21555             this.ownerTree = tree;
21556             var cs = this.childNodes;
21557             for(var i = 0, len = cs.length; i < len; i++) {
21558                 cs[i].setOwnerTree(tree);
21559             }
21560             if(tree){
21561                 tree.registerNode(this);
21562             }
21563         }
21564     },
21565
21566     /**
21567      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21568      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21569      * @return {String} The path
21570      */
21571     getPath : function(attr){
21572         attr = attr || "id";
21573         var p = this.parentNode;
21574         var b = [this.attributes[attr]];
21575         while(p){
21576             b.unshift(p.attributes[attr]);
21577             p = p.parentNode;
21578         }
21579         var sep = this.getOwnerTree().pathSeparator;
21580         return sep + b.join(sep);
21581     },
21582
21583     /**
21584      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21585      * function call will be the scope provided or the current node. The arguments to the function
21586      * will be the args provided or the current node. If the function returns false at any point,
21587      * the bubble is stopped.
21588      * @param {Function} fn The function to call
21589      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21590      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21591      */
21592     bubble : function(fn, scope, args){
21593         var p = this;
21594         while(p){
21595             if(fn.call(scope || p, args || p) === false){
21596                 break;
21597             }
21598             p = p.parentNode;
21599         }
21600     },
21601
21602     /**
21603      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21604      * function call will be the scope provided or the current node. The arguments to the function
21605      * will be the args provided or the current node. If the function returns false at any point,
21606      * the cascade is stopped on that branch.
21607      * @param {Function} fn The function to call
21608      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21609      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21610      */
21611     cascade : function(fn, scope, args){
21612         if(fn.call(scope || this, args || this) !== false){
21613             var cs = this.childNodes;
21614             for(var i = 0, len = cs.length; i < len; i++) {
21615                 cs[i].cascade(fn, scope, args);
21616             }
21617         }
21618     },
21619
21620     /**
21621      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21622      * function call will be the scope provided or the current node. The arguments to the function
21623      * will be the args provided or the current node. If the function returns false at any point,
21624      * the iteration stops.
21625      * @param {Function} fn The function to call
21626      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21627      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21628      */
21629     eachChild : function(fn, scope, args){
21630         var cs = this.childNodes;
21631         for(var i = 0, len = cs.length; i < len; i++) {
21632                 if(fn.call(scope || this, args || cs[i]) === false){
21633                     break;
21634                 }
21635         }
21636     },
21637
21638     /**
21639      * Finds the first child that has the attribute with the specified value.
21640      * @param {String} attribute The attribute name
21641      * @param {Mixed} value The value to search for
21642      * @return {Node} The found child or null if none was found
21643      */
21644     findChild : function(attribute, value){
21645         var cs = this.childNodes;
21646         for(var i = 0, len = cs.length; i < len; i++) {
21647                 if(cs[i].attributes[attribute] == value){
21648                     return cs[i];
21649                 }
21650         }
21651         return null;
21652     },
21653
21654     /**
21655      * Finds the first child by a custom function. The child matches if the function passed
21656      * returns true.
21657      * @param {Function} fn
21658      * @param {Object} scope (optional)
21659      * @return {Node} The found child or null if none was found
21660      */
21661     findChildBy : function(fn, scope){
21662         var cs = this.childNodes;
21663         for(var i = 0, len = cs.length; i < len; i++) {
21664                 if(fn.call(scope||cs[i], cs[i]) === true){
21665                     return cs[i];
21666                 }
21667         }
21668         return null;
21669     },
21670
21671     /**
21672      * Sorts this nodes children using the supplied sort function
21673      * @param {Function} fn
21674      * @param {Object} scope (optional)
21675      */
21676     sort : function(fn, scope){
21677         var cs = this.childNodes;
21678         var len = cs.length;
21679         if(len > 0){
21680             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21681             cs.sort(sortFn);
21682             for(var i = 0; i < len; i++){
21683                 var n = cs[i];
21684                 n.previousSibling = cs[i-1];
21685                 n.nextSibling = cs[i+1];
21686                 if(i == 0){
21687                     this.setFirstChild(n);
21688                 }
21689                 if(i == len-1){
21690                     this.setLastChild(n);
21691                 }
21692             }
21693         }
21694     },
21695
21696     /**
21697      * Returns true if this node is an ancestor (at any point) of the passed node.
21698      * @param {Node} node
21699      * @return {Boolean}
21700      */
21701     contains : function(node){
21702         return node.isAncestor(this);
21703     },
21704
21705     /**
21706      * Returns true if the passed node is an ancestor (at any point) of this node.
21707      * @param {Node} node
21708      * @return {Boolean}
21709      */
21710     isAncestor : function(node){
21711         var p = this.parentNode;
21712         while(p){
21713             if(p == node){
21714                 return true;
21715             }
21716             p = p.parentNode;
21717         }
21718         return false;
21719     },
21720
21721     toString : function(){
21722         return "[Node"+(this.id?" "+this.id:"")+"]";
21723     }
21724 });/*
21725  * Based on:
21726  * Ext JS Library 1.1.1
21727  * Copyright(c) 2006-2007, Ext JS, LLC.
21728  *
21729  * Originally Released Under LGPL - original licence link has changed is not relivant.
21730  *
21731  * Fork - LGPL
21732  * <script type="text/javascript">
21733  */
21734  
21735
21736 /**
21737  * @class Roo.ComponentMgr
21738  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21739  * @singleton
21740  */
21741 Roo.ComponentMgr = function(){
21742     var all = new Roo.util.MixedCollection();
21743
21744     return {
21745         /**
21746          * Registers a component.
21747          * @param {Roo.Component} c The component
21748          */
21749         register : function(c){
21750             all.add(c);
21751         },
21752
21753         /**
21754          * Unregisters a component.
21755          * @param {Roo.Component} c The component
21756          */
21757         unregister : function(c){
21758             all.remove(c);
21759         },
21760
21761         /**
21762          * Returns a component by id
21763          * @param {String} id The component id
21764          */
21765         get : function(id){
21766             return all.get(id);
21767         },
21768
21769         /**
21770          * Registers a function that will be called when a specified component is added to ComponentMgr
21771          * @param {String} id The component id
21772          * @param {Funtction} fn The callback function
21773          * @param {Object} scope The scope of the callback
21774          */
21775         onAvailable : function(id, fn, scope){
21776             all.on("add", function(index, o){
21777                 if(o.id == id){
21778                     fn.call(scope || o, o);
21779                     all.un("add", fn, scope);
21780                 }
21781             });
21782         }
21783     };
21784 }();/*
21785  * Based on:
21786  * Ext JS Library 1.1.1
21787  * Copyright(c) 2006-2007, Ext JS, LLC.
21788  *
21789  * Originally Released Under LGPL - original licence link has changed is not relivant.
21790  *
21791  * Fork - LGPL
21792  * <script type="text/javascript">
21793  */
21794  
21795 /**
21796  * @class Roo.Component
21797  * @extends Roo.util.Observable
21798  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21799  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21800  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21801  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21802  * All visual components (widgets) that require rendering into a layout should subclass Component.
21803  * @constructor
21804  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21805  * 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
21806  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21807  */
21808 Roo.Component = function(config){
21809     config = config || {};
21810     if(config.tagName || config.dom || typeof config == "string"){ // element object
21811         config = {el: config, id: config.id || config};
21812     }
21813     this.initialConfig = config;
21814
21815     Roo.apply(this, config);
21816     this.addEvents({
21817         /**
21818          * @event disable
21819          * Fires after the component is disabled.
21820              * @param {Roo.Component} this
21821              */
21822         disable : true,
21823         /**
21824          * @event enable
21825          * Fires after the component is enabled.
21826              * @param {Roo.Component} this
21827              */
21828         enable : true,
21829         /**
21830          * @event beforeshow
21831          * Fires before the component is shown.  Return false to stop the show.
21832              * @param {Roo.Component} this
21833              */
21834         beforeshow : true,
21835         /**
21836          * @event show
21837          * Fires after the component is shown.
21838              * @param {Roo.Component} this
21839              */
21840         show : true,
21841         /**
21842          * @event beforehide
21843          * Fires before the component is hidden. Return false to stop the hide.
21844              * @param {Roo.Component} this
21845              */
21846         beforehide : true,
21847         /**
21848          * @event hide
21849          * Fires after the component is hidden.
21850              * @param {Roo.Component} this
21851              */
21852         hide : true,
21853         /**
21854          * @event beforerender
21855          * Fires before the component is rendered. Return false to stop the render.
21856              * @param {Roo.Component} this
21857              */
21858         beforerender : true,
21859         /**
21860          * @event render
21861          * Fires after the component is rendered.
21862              * @param {Roo.Component} this
21863              */
21864         render : true,
21865         /**
21866          * @event beforedestroy
21867          * Fires before the component is destroyed. Return false to stop the destroy.
21868              * @param {Roo.Component} this
21869              */
21870         beforedestroy : true,
21871         /**
21872          * @event destroy
21873          * Fires after the component is destroyed.
21874              * @param {Roo.Component} this
21875              */
21876         destroy : true
21877     });
21878     if(!this.id){
21879         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21880     }
21881     Roo.ComponentMgr.register(this);
21882     Roo.Component.superclass.constructor.call(this);
21883     this.initComponent();
21884     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21885         this.render(this.renderTo);
21886         delete this.renderTo;
21887     }
21888 };
21889
21890 /** @private */
21891 Roo.Component.AUTO_ID = 1000;
21892
21893 Roo.extend(Roo.Component, Roo.util.Observable, {
21894     /**
21895      * @scope Roo.Component.prototype
21896      * @type {Boolean}
21897      * true if this component is hidden. Read-only.
21898      */
21899     hidden : false,
21900     /**
21901      * @type {Boolean}
21902      * true if this component is disabled. Read-only.
21903      */
21904     disabled : false,
21905     /**
21906      * @type {Boolean}
21907      * true if this component has been rendered. Read-only.
21908      */
21909     rendered : false,
21910     
21911     /** @cfg {String} disableClass
21912      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21913      */
21914     disabledClass : "x-item-disabled",
21915         /** @cfg {Boolean} allowDomMove
21916          * Whether the component can move the Dom node when rendering (defaults to true).
21917          */
21918     allowDomMove : true,
21919     /** @cfg {String} hideMode
21920      * How this component should hidden. Supported values are
21921      * "visibility" (css visibility), "offsets" (negative offset position) and
21922      * "display" (css display) - defaults to "display".
21923      */
21924     hideMode: 'display',
21925
21926     /** @private */
21927     ctype : "Roo.Component",
21928
21929     /**
21930      * @cfg {String} actionMode 
21931      * which property holds the element that used for  hide() / show() / disable() / enable()
21932      * default is 'el' 
21933      */
21934     actionMode : "el",
21935
21936     /** @private */
21937     getActionEl : function(){
21938         return this[this.actionMode];
21939     },
21940
21941     initComponent : Roo.emptyFn,
21942     /**
21943      * If this is a lazy rendering component, render it to its container element.
21944      * @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.
21945      */
21946     render : function(container, position){
21947         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21948             if(!container && this.el){
21949                 this.el = Roo.get(this.el);
21950                 container = this.el.dom.parentNode;
21951                 this.allowDomMove = false;
21952             }
21953             this.container = Roo.get(container);
21954             this.rendered = true;
21955             if(position !== undefined){
21956                 if(typeof position == 'number'){
21957                     position = this.container.dom.childNodes[position];
21958                 }else{
21959                     position = Roo.getDom(position);
21960                 }
21961             }
21962             this.onRender(this.container, position || null);
21963             if(this.cls){
21964                 this.el.addClass(this.cls);
21965                 delete this.cls;
21966             }
21967             if(this.style){
21968                 this.el.applyStyles(this.style);
21969                 delete this.style;
21970             }
21971             this.fireEvent("render", this);
21972             this.afterRender(this.container);
21973             if(this.hidden){
21974                 this.hide();
21975             }
21976             if(this.disabled){
21977                 this.disable();
21978             }
21979         }
21980         return this;
21981     },
21982
21983     /** @private */
21984     // default function is not really useful
21985     onRender : function(ct, position){
21986         if(this.el){
21987             this.el = Roo.get(this.el);
21988             if(this.allowDomMove !== false){
21989                 ct.dom.insertBefore(this.el.dom, position);
21990             }
21991         }
21992     },
21993
21994     /** @private */
21995     getAutoCreate : function(){
21996         var cfg = typeof this.autoCreate == "object" ?
21997                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21998         if(this.id && !cfg.id){
21999             cfg.id = this.id;
22000         }
22001         return cfg;
22002     },
22003
22004     /** @private */
22005     afterRender : Roo.emptyFn,
22006
22007     /**
22008      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22009      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22010      */
22011     destroy : function(){
22012         if(this.fireEvent("beforedestroy", this) !== false){
22013             this.purgeListeners();
22014             this.beforeDestroy();
22015             if(this.rendered){
22016                 this.el.removeAllListeners();
22017                 this.el.remove();
22018                 if(this.actionMode == "container"){
22019                     this.container.remove();
22020                 }
22021             }
22022             this.onDestroy();
22023             Roo.ComponentMgr.unregister(this);
22024             this.fireEvent("destroy", this);
22025         }
22026     },
22027
22028         /** @private */
22029     beforeDestroy : function(){
22030
22031     },
22032
22033         /** @private */
22034         onDestroy : function(){
22035
22036     },
22037
22038     /**
22039      * Returns the underlying {@link Roo.Element}.
22040      * @return {Roo.Element} The element
22041      */
22042     getEl : function(){
22043         return this.el;
22044     },
22045
22046     /**
22047      * Returns the id of this component.
22048      * @return {String}
22049      */
22050     getId : function(){
22051         return this.id;
22052     },
22053
22054     /**
22055      * Try to focus this component.
22056      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22057      * @return {Roo.Component} this
22058      */
22059     focus : function(selectText){
22060         if(this.rendered){
22061             this.el.focus();
22062             if(selectText === true){
22063                 this.el.dom.select();
22064             }
22065         }
22066         return this;
22067     },
22068
22069     /** @private */
22070     blur : function(){
22071         if(this.rendered){
22072             this.el.blur();
22073         }
22074         return this;
22075     },
22076
22077     /**
22078      * Disable this component.
22079      * @return {Roo.Component} this
22080      */
22081     disable : function(){
22082         if(this.rendered){
22083             this.onDisable();
22084         }
22085         this.disabled = true;
22086         this.fireEvent("disable", this);
22087         return this;
22088     },
22089
22090         // private
22091     onDisable : function(){
22092         this.getActionEl().addClass(this.disabledClass);
22093         this.el.dom.disabled = true;
22094     },
22095
22096     /**
22097      * Enable this component.
22098      * @return {Roo.Component} this
22099      */
22100     enable : function(){
22101         if(this.rendered){
22102             this.onEnable();
22103         }
22104         this.disabled = false;
22105         this.fireEvent("enable", this);
22106         return this;
22107     },
22108
22109         // private
22110     onEnable : function(){
22111         this.getActionEl().removeClass(this.disabledClass);
22112         this.el.dom.disabled = false;
22113     },
22114
22115     /**
22116      * Convenience function for setting disabled/enabled by boolean.
22117      * @param {Boolean} disabled
22118      */
22119     setDisabled : function(disabled){
22120         this[disabled ? "disable" : "enable"]();
22121     },
22122
22123     /**
22124      * Show this component.
22125      * @return {Roo.Component} this
22126      */
22127     show: function(){
22128         if(this.fireEvent("beforeshow", this) !== false){
22129             this.hidden = false;
22130             if(this.rendered){
22131                 this.onShow();
22132             }
22133             this.fireEvent("show", this);
22134         }
22135         return this;
22136     },
22137
22138     // private
22139     onShow : function(){
22140         var ae = this.getActionEl();
22141         if(this.hideMode == 'visibility'){
22142             ae.dom.style.visibility = "visible";
22143         }else if(this.hideMode == 'offsets'){
22144             ae.removeClass('x-hidden');
22145         }else{
22146             ae.dom.style.display = "";
22147         }
22148     },
22149
22150     /**
22151      * Hide this component.
22152      * @return {Roo.Component} this
22153      */
22154     hide: function(){
22155         if(this.fireEvent("beforehide", this) !== false){
22156             this.hidden = true;
22157             if(this.rendered){
22158                 this.onHide();
22159             }
22160             this.fireEvent("hide", this);
22161         }
22162         return this;
22163     },
22164
22165     // private
22166     onHide : function(){
22167         var ae = this.getActionEl();
22168         if(this.hideMode == 'visibility'){
22169             ae.dom.style.visibility = "hidden";
22170         }else if(this.hideMode == 'offsets'){
22171             ae.addClass('x-hidden');
22172         }else{
22173             ae.dom.style.display = "none";
22174         }
22175     },
22176
22177     /**
22178      * Convenience function to hide or show this component by boolean.
22179      * @param {Boolean} visible True to show, false to hide
22180      * @return {Roo.Component} this
22181      */
22182     setVisible: function(visible){
22183         if(visible) {
22184             this.show();
22185         }else{
22186             this.hide();
22187         }
22188         return this;
22189     },
22190
22191     /**
22192      * Returns true if this component is visible.
22193      */
22194     isVisible : function(){
22195         return this.getActionEl().isVisible();
22196     },
22197
22198     cloneConfig : function(overrides){
22199         overrides = overrides || {};
22200         var id = overrides.id || Roo.id();
22201         var cfg = Roo.applyIf(overrides, this.initialConfig);
22202         cfg.id = id; // prevent dup id
22203         return new this.constructor(cfg);
22204     }
22205 });/*
22206  * Based on:
22207  * Ext JS Library 1.1.1
22208  * Copyright(c) 2006-2007, Ext JS, LLC.
22209  *
22210  * Originally Released Under LGPL - original licence link has changed is not relivant.
22211  *
22212  * Fork - LGPL
22213  * <script type="text/javascript">
22214  */
22215  (function(){ 
22216 /**
22217  * @class Roo.Layer
22218  * @extends Roo.Element
22219  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22220  * automatic maintaining of shadow/shim positions.
22221  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22222  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22223  * you can pass a string with a CSS class name. False turns off the shadow.
22224  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22225  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22226  * @cfg {String} cls CSS class to add to the element
22227  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22228  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22229  * @constructor
22230  * @param {Object} config An object with config options.
22231  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22232  */
22233
22234 Roo.Layer = function(config, existingEl){
22235     config = config || {};
22236     var dh = Roo.DomHelper;
22237     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22238     if(existingEl){
22239         this.dom = Roo.getDom(existingEl);
22240     }
22241     if(!this.dom){
22242         var o = config.dh || {tag: "div", cls: "x-layer"};
22243         this.dom = dh.append(pel, o);
22244     }
22245     if(config.cls){
22246         this.addClass(config.cls);
22247     }
22248     this.constrain = config.constrain !== false;
22249     this.visibilityMode = Roo.Element.VISIBILITY;
22250     if(config.id){
22251         this.id = this.dom.id = config.id;
22252     }else{
22253         this.id = Roo.id(this.dom);
22254     }
22255     this.zindex = config.zindex || this.getZIndex();
22256     this.position("absolute", this.zindex);
22257     if(config.shadow){
22258         this.shadowOffset = config.shadowOffset || 4;
22259         this.shadow = new Roo.Shadow({
22260             offset : this.shadowOffset,
22261             mode : config.shadow
22262         });
22263     }else{
22264         this.shadowOffset = 0;
22265     }
22266     this.useShim = config.shim !== false && Roo.useShims;
22267     this.useDisplay = config.useDisplay;
22268     this.hide();
22269 };
22270
22271 var supr = Roo.Element.prototype;
22272
22273 // shims are shared among layer to keep from having 100 iframes
22274 var shims = [];
22275
22276 Roo.extend(Roo.Layer, Roo.Element, {
22277
22278     getZIndex : function(){
22279         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22280     },
22281
22282     getShim : function(){
22283         if(!this.useShim){
22284             return null;
22285         }
22286         if(this.shim){
22287             return this.shim;
22288         }
22289         var shim = shims.shift();
22290         if(!shim){
22291             shim = this.createShim();
22292             shim.enableDisplayMode('block');
22293             shim.dom.style.display = 'none';
22294             shim.dom.style.visibility = 'visible';
22295         }
22296         var pn = this.dom.parentNode;
22297         if(shim.dom.parentNode != pn){
22298             pn.insertBefore(shim.dom, this.dom);
22299         }
22300         shim.setStyle('z-index', this.getZIndex()-2);
22301         this.shim = shim;
22302         return shim;
22303     },
22304
22305     hideShim : function(){
22306         if(this.shim){
22307             this.shim.setDisplayed(false);
22308             shims.push(this.shim);
22309             delete this.shim;
22310         }
22311     },
22312
22313     disableShadow : function(){
22314         if(this.shadow){
22315             this.shadowDisabled = true;
22316             this.shadow.hide();
22317             this.lastShadowOffset = this.shadowOffset;
22318             this.shadowOffset = 0;
22319         }
22320     },
22321
22322     enableShadow : function(show){
22323         if(this.shadow){
22324             this.shadowDisabled = false;
22325             this.shadowOffset = this.lastShadowOffset;
22326             delete this.lastShadowOffset;
22327             if(show){
22328                 this.sync(true);
22329             }
22330         }
22331     },
22332
22333     // private
22334     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22335     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22336     sync : function(doShow){
22337         var sw = this.shadow;
22338         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22339             var sh = this.getShim();
22340
22341             var w = this.getWidth(),
22342                 h = this.getHeight();
22343
22344             var l = this.getLeft(true),
22345                 t = this.getTop(true);
22346
22347             if(sw && !this.shadowDisabled){
22348                 if(doShow && !sw.isVisible()){
22349                     sw.show(this);
22350                 }else{
22351                     sw.realign(l, t, w, h);
22352                 }
22353                 if(sh){
22354                     if(doShow){
22355                        sh.show();
22356                     }
22357                     // fit the shim behind the shadow, so it is shimmed too
22358                     var a = sw.adjusts, s = sh.dom.style;
22359                     s.left = (Math.min(l, l+a.l))+"px";
22360                     s.top = (Math.min(t, t+a.t))+"px";
22361                     s.width = (w+a.w)+"px";
22362                     s.height = (h+a.h)+"px";
22363                 }
22364             }else if(sh){
22365                 if(doShow){
22366                    sh.show();
22367                 }
22368                 sh.setSize(w, h);
22369                 sh.setLeftTop(l, t);
22370             }
22371             
22372         }
22373     },
22374
22375     // private
22376     destroy : function(){
22377         this.hideShim();
22378         if(this.shadow){
22379             this.shadow.hide();
22380         }
22381         this.removeAllListeners();
22382         var pn = this.dom.parentNode;
22383         if(pn){
22384             pn.removeChild(this.dom);
22385         }
22386         Roo.Element.uncache(this.id);
22387     },
22388
22389     remove : function(){
22390         this.destroy();
22391     },
22392
22393     // private
22394     beginUpdate : function(){
22395         this.updating = true;
22396     },
22397
22398     // private
22399     endUpdate : function(){
22400         this.updating = false;
22401         this.sync(true);
22402     },
22403
22404     // private
22405     hideUnders : function(negOffset){
22406         if(this.shadow){
22407             this.shadow.hide();
22408         }
22409         this.hideShim();
22410     },
22411
22412     // private
22413     constrainXY : function(){
22414         if(this.constrain){
22415             var vw = Roo.lib.Dom.getViewWidth(),
22416                 vh = Roo.lib.Dom.getViewHeight();
22417             var s = Roo.get(document).getScroll();
22418
22419             var xy = this.getXY();
22420             var x = xy[0], y = xy[1];   
22421             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22422             // only move it if it needs it
22423             var moved = false;
22424             // first validate right/bottom
22425             if((x + w) > vw+s.left){
22426                 x = vw - w - this.shadowOffset;
22427                 moved = true;
22428             }
22429             if((y + h) > vh+s.top){
22430                 y = vh - h - this.shadowOffset;
22431                 moved = true;
22432             }
22433             // then make sure top/left isn't negative
22434             if(x < s.left){
22435                 x = s.left;
22436                 moved = true;
22437             }
22438             if(y < s.top){
22439                 y = s.top;
22440                 moved = true;
22441             }
22442             if(moved){
22443                 if(this.avoidY){
22444                     var ay = this.avoidY;
22445                     if(y <= ay && (y+h) >= ay){
22446                         y = ay-h-5;   
22447                     }
22448                 }
22449                 xy = [x, y];
22450                 this.storeXY(xy);
22451                 supr.setXY.call(this, xy);
22452                 this.sync();
22453             }
22454         }
22455     },
22456
22457     isVisible : function(){
22458         return this.visible;    
22459     },
22460
22461     // private
22462     showAction : function(){
22463         this.visible = true; // track visibility to prevent getStyle calls
22464         if(this.useDisplay === true){
22465             this.setDisplayed("");
22466         }else if(this.lastXY){
22467             supr.setXY.call(this, this.lastXY);
22468         }else if(this.lastLT){
22469             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22470         }
22471     },
22472
22473     // private
22474     hideAction : function(){
22475         this.visible = false;
22476         if(this.useDisplay === true){
22477             this.setDisplayed(false);
22478         }else{
22479             this.setLeftTop(-10000,-10000);
22480         }
22481     },
22482
22483     // overridden Element method
22484     setVisible : function(v, a, d, c, e){
22485         if(v){
22486             this.showAction();
22487         }
22488         if(a && v){
22489             var cb = function(){
22490                 this.sync(true);
22491                 if(c){
22492                     c();
22493                 }
22494             }.createDelegate(this);
22495             supr.setVisible.call(this, true, true, d, cb, e);
22496         }else{
22497             if(!v){
22498                 this.hideUnders(true);
22499             }
22500             var cb = c;
22501             if(a){
22502                 cb = function(){
22503                     this.hideAction();
22504                     if(c){
22505                         c();
22506                     }
22507                 }.createDelegate(this);
22508             }
22509             supr.setVisible.call(this, v, a, d, cb, e);
22510             if(v){
22511                 this.sync(true);
22512             }else if(!a){
22513                 this.hideAction();
22514             }
22515         }
22516     },
22517
22518     storeXY : function(xy){
22519         delete this.lastLT;
22520         this.lastXY = xy;
22521     },
22522
22523     storeLeftTop : function(left, top){
22524         delete this.lastXY;
22525         this.lastLT = [left, top];
22526     },
22527
22528     // private
22529     beforeFx : function(){
22530         this.beforeAction();
22531         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22532     },
22533
22534     // private
22535     afterFx : function(){
22536         Roo.Layer.superclass.afterFx.apply(this, arguments);
22537         this.sync(this.isVisible());
22538     },
22539
22540     // private
22541     beforeAction : function(){
22542         if(!this.updating && this.shadow){
22543             this.shadow.hide();
22544         }
22545     },
22546
22547     // overridden Element method
22548     setLeft : function(left){
22549         this.storeLeftTop(left, this.getTop(true));
22550         supr.setLeft.apply(this, arguments);
22551         this.sync();
22552     },
22553
22554     setTop : function(top){
22555         this.storeLeftTop(this.getLeft(true), top);
22556         supr.setTop.apply(this, arguments);
22557         this.sync();
22558     },
22559
22560     setLeftTop : function(left, top){
22561         this.storeLeftTop(left, top);
22562         supr.setLeftTop.apply(this, arguments);
22563         this.sync();
22564     },
22565
22566     setXY : function(xy, a, d, c, e){
22567         this.fixDisplay();
22568         this.beforeAction();
22569         this.storeXY(xy);
22570         var cb = this.createCB(c);
22571         supr.setXY.call(this, xy, a, d, cb, e);
22572         if(!a){
22573             cb();
22574         }
22575     },
22576
22577     // private
22578     createCB : function(c){
22579         var el = this;
22580         return function(){
22581             el.constrainXY();
22582             el.sync(true);
22583             if(c){
22584                 c();
22585             }
22586         };
22587     },
22588
22589     // overridden Element method
22590     setX : function(x, a, d, c, e){
22591         this.setXY([x, this.getY()], a, d, c, e);
22592     },
22593
22594     // overridden Element method
22595     setY : function(y, a, d, c, e){
22596         this.setXY([this.getX(), y], a, d, c, e);
22597     },
22598
22599     // overridden Element method
22600     setSize : function(w, h, a, d, c, e){
22601         this.beforeAction();
22602         var cb = this.createCB(c);
22603         supr.setSize.call(this, w, h, a, d, cb, e);
22604         if(!a){
22605             cb();
22606         }
22607     },
22608
22609     // overridden Element method
22610     setWidth : function(w, a, d, c, e){
22611         this.beforeAction();
22612         var cb = this.createCB(c);
22613         supr.setWidth.call(this, w, a, d, cb, e);
22614         if(!a){
22615             cb();
22616         }
22617     },
22618
22619     // overridden Element method
22620     setHeight : function(h, a, d, c, e){
22621         this.beforeAction();
22622         var cb = this.createCB(c);
22623         supr.setHeight.call(this, h, a, d, cb, e);
22624         if(!a){
22625             cb();
22626         }
22627     },
22628
22629     // overridden Element method
22630     setBounds : function(x, y, w, h, a, d, c, e){
22631         this.beforeAction();
22632         var cb = this.createCB(c);
22633         if(!a){
22634             this.storeXY([x, y]);
22635             supr.setXY.call(this, [x, y]);
22636             supr.setSize.call(this, w, h, a, d, cb, e);
22637             cb();
22638         }else{
22639             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22640         }
22641         return this;
22642     },
22643     
22644     /**
22645      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22646      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22647      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22648      * @param {Number} zindex The new z-index to set
22649      * @return {this} The Layer
22650      */
22651     setZIndex : function(zindex){
22652         this.zindex = zindex;
22653         this.setStyle("z-index", zindex + 2);
22654         if(this.shadow){
22655             this.shadow.setZIndex(zindex + 1);
22656         }
22657         if(this.shim){
22658             this.shim.setStyle("z-index", zindex);
22659         }
22660     }
22661 });
22662 })();/*
22663  * Based on:
22664  * Ext JS Library 1.1.1
22665  * Copyright(c) 2006-2007, Ext JS, LLC.
22666  *
22667  * Originally Released Under LGPL - original licence link has changed is not relivant.
22668  *
22669  * Fork - LGPL
22670  * <script type="text/javascript">
22671  */
22672
22673
22674 /**
22675  * @class Roo.Shadow
22676  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22677  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22678  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22679  * @constructor
22680  * Create a new Shadow
22681  * @param {Object} config The config object
22682  */
22683 Roo.Shadow = function(config){
22684     Roo.apply(this, config);
22685     if(typeof this.mode != "string"){
22686         this.mode = this.defaultMode;
22687     }
22688     var o = this.offset, a = {h: 0};
22689     var rad = Math.floor(this.offset/2);
22690     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22691         case "drop":
22692             a.w = 0;
22693             a.l = a.t = o;
22694             a.t -= 1;
22695             if(Roo.isIE){
22696                 a.l -= this.offset + rad;
22697                 a.t -= this.offset + rad;
22698                 a.w -= rad;
22699                 a.h -= rad;
22700                 a.t += 1;
22701             }
22702         break;
22703         case "sides":
22704             a.w = (o*2);
22705             a.l = -o;
22706             a.t = o-1;
22707             if(Roo.isIE){
22708                 a.l -= (this.offset - rad);
22709                 a.t -= this.offset + rad;
22710                 a.l += 1;
22711                 a.w -= (this.offset - rad)*2;
22712                 a.w -= rad + 1;
22713                 a.h -= 1;
22714             }
22715         break;
22716         case "frame":
22717             a.w = a.h = (o*2);
22718             a.l = a.t = -o;
22719             a.t += 1;
22720             a.h -= 2;
22721             if(Roo.isIE){
22722                 a.l -= (this.offset - rad);
22723                 a.t -= (this.offset - rad);
22724                 a.l += 1;
22725                 a.w -= (this.offset + rad + 1);
22726                 a.h -= (this.offset + rad);
22727                 a.h += 1;
22728             }
22729         break;
22730     };
22731
22732     this.adjusts = a;
22733 };
22734
22735 Roo.Shadow.prototype = {
22736     /**
22737      * @cfg {String} mode
22738      * The shadow display mode.  Supports the following options:<br />
22739      * sides: Shadow displays on both sides and bottom only<br />
22740      * frame: Shadow displays equally on all four sides<br />
22741      * drop: Traditional bottom-right drop shadow (default)
22742      */
22743     /**
22744      * @cfg {String} offset
22745      * The number of pixels to offset the shadow from the element (defaults to 4)
22746      */
22747     offset: 4,
22748
22749     // private
22750     defaultMode: "drop",
22751
22752     /**
22753      * Displays the shadow under the target element
22754      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22755      */
22756     show : function(target){
22757         target = Roo.get(target);
22758         if(!this.el){
22759             this.el = Roo.Shadow.Pool.pull();
22760             if(this.el.dom.nextSibling != target.dom){
22761                 this.el.insertBefore(target);
22762             }
22763         }
22764         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22765         if(Roo.isIE){
22766             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22767         }
22768         this.realign(
22769             target.getLeft(true),
22770             target.getTop(true),
22771             target.getWidth(),
22772             target.getHeight()
22773         );
22774         this.el.dom.style.display = "block";
22775     },
22776
22777     /**
22778      * Returns true if the shadow is visible, else false
22779      */
22780     isVisible : function(){
22781         return this.el ? true : false;  
22782     },
22783
22784     /**
22785      * Direct alignment when values are already available. Show must be called at least once before
22786      * calling this method to ensure it is initialized.
22787      * @param {Number} left The target element left position
22788      * @param {Number} top The target element top position
22789      * @param {Number} width The target element width
22790      * @param {Number} height The target element height
22791      */
22792     realign : function(l, t, w, h){
22793         if(!this.el){
22794             return;
22795         }
22796         var a = this.adjusts, d = this.el.dom, s = d.style;
22797         var iea = 0;
22798         s.left = (l+a.l)+"px";
22799         s.top = (t+a.t)+"px";
22800         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22801  
22802         if(s.width != sws || s.height != shs){
22803             s.width = sws;
22804             s.height = shs;
22805             if(!Roo.isIE){
22806                 var cn = d.childNodes;
22807                 var sww = Math.max(0, (sw-12))+"px";
22808                 cn[0].childNodes[1].style.width = sww;
22809                 cn[1].childNodes[1].style.width = sww;
22810                 cn[2].childNodes[1].style.width = sww;
22811                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22812             }
22813         }
22814     },
22815
22816     /**
22817      * Hides this shadow
22818      */
22819     hide : function(){
22820         if(this.el){
22821             this.el.dom.style.display = "none";
22822             Roo.Shadow.Pool.push(this.el);
22823             delete this.el;
22824         }
22825     },
22826
22827     /**
22828      * Adjust the z-index of this shadow
22829      * @param {Number} zindex The new z-index
22830      */
22831     setZIndex : function(z){
22832         this.zIndex = z;
22833         if(this.el){
22834             this.el.setStyle("z-index", z);
22835         }
22836     }
22837 };
22838
22839 // Private utility class that manages the internal Shadow cache
22840 Roo.Shadow.Pool = function(){
22841     var p = [];
22842     var markup = Roo.isIE ?
22843                  '<div class="x-ie-shadow"></div>' :
22844                  '<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>';
22845     return {
22846         pull : function(){
22847             var sh = p.shift();
22848             if(!sh){
22849                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22850                 sh.autoBoxAdjust = false;
22851             }
22852             return sh;
22853         },
22854
22855         push : function(sh){
22856             p.push(sh);
22857         }
22858     };
22859 }();/*
22860  * Based on:
22861  * Ext JS Library 1.1.1
22862  * Copyright(c) 2006-2007, Ext JS, LLC.
22863  *
22864  * Originally Released Under LGPL - original licence link has changed is not relivant.
22865  *
22866  * Fork - LGPL
22867  * <script type="text/javascript">
22868  */
22869
22870 /**
22871  * @class Roo.BoxComponent
22872  * @extends Roo.Component
22873  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22874  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22875  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22876  * layout containers.
22877  * @constructor
22878  * @param {Roo.Element/String/Object} config The configuration options.
22879  */
22880 Roo.BoxComponent = function(config){
22881     Roo.Component.call(this, config);
22882     this.addEvents({
22883         /**
22884          * @event resize
22885          * Fires after the component is resized.
22886              * @param {Roo.Component} this
22887              * @param {Number} adjWidth The box-adjusted width that was set
22888              * @param {Number} adjHeight The box-adjusted height that was set
22889              * @param {Number} rawWidth The width that was originally specified
22890              * @param {Number} rawHeight The height that was originally specified
22891              */
22892         resize : true,
22893         /**
22894          * @event move
22895          * Fires after the component is moved.
22896              * @param {Roo.Component} this
22897              * @param {Number} x The new x position
22898              * @param {Number} y The new y position
22899              */
22900         move : true
22901     });
22902 };
22903
22904 Roo.extend(Roo.BoxComponent, Roo.Component, {
22905     // private, set in afterRender to signify that the component has been rendered
22906     boxReady : false,
22907     // private, used to defer height settings to subclasses
22908     deferHeight: false,
22909     /** @cfg {Number} width
22910      * width (optional) size of component
22911      */
22912      /** @cfg {Number} height
22913      * height (optional) size of component
22914      */
22915      
22916     /**
22917      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22918      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22919      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22920      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22921      * @return {Roo.BoxComponent} this
22922      */
22923     setSize : function(w, h){
22924         // support for standard size objects
22925         if(typeof w == 'object'){
22926             h = w.height;
22927             w = w.width;
22928         }
22929         // not rendered
22930         if(!this.boxReady){
22931             this.width = w;
22932             this.height = h;
22933             return this;
22934         }
22935
22936         // prevent recalcs when not needed
22937         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22938             return this;
22939         }
22940         this.lastSize = {width: w, height: h};
22941
22942         var adj = this.adjustSize(w, h);
22943         var aw = adj.width, ah = adj.height;
22944         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22945             var rz = this.getResizeEl();
22946             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22947                 rz.setSize(aw, ah);
22948             }else if(!this.deferHeight && ah !== undefined){
22949                 rz.setHeight(ah);
22950             }else if(aw !== undefined){
22951                 rz.setWidth(aw);
22952             }
22953             this.onResize(aw, ah, w, h);
22954             this.fireEvent('resize', this, aw, ah, w, h);
22955         }
22956         return this;
22957     },
22958
22959     /**
22960      * Gets the current size of the component's underlying element.
22961      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22962      */
22963     getSize : function(){
22964         return this.el.getSize();
22965     },
22966
22967     /**
22968      * Gets the current XY position of the component's underlying element.
22969      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22970      * @return {Array} The XY position of the element (e.g., [100, 200])
22971      */
22972     getPosition : function(local){
22973         if(local === true){
22974             return [this.el.getLeft(true), this.el.getTop(true)];
22975         }
22976         return this.xy || this.el.getXY();
22977     },
22978
22979     /**
22980      * Gets the current box measurements of the component's underlying element.
22981      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22982      * @returns {Object} box An object in the format {x, y, width, height}
22983      */
22984     getBox : function(local){
22985         var s = this.el.getSize();
22986         if(local){
22987             s.x = this.el.getLeft(true);
22988             s.y = this.el.getTop(true);
22989         }else{
22990             var xy = this.xy || this.el.getXY();
22991             s.x = xy[0];
22992             s.y = xy[1];
22993         }
22994         return s;
22995     },
22996
22997     /**
22998      * Sets the current box measurements of the component's underlying element.
22999      * @param {Object} box An object in the format {x, y, width, height}
23000      * @returns {Roo.BoxComponent} this
23001      */
23002     updateBox : function(box){
23003         this.setSize(box.width, box.height);
23004         this.setPagePosition(box.x, box.y);
23005         return this;
23006     },
23007
23008     // protected
23009     getResizeEl : function(){
23010         return this.resizeEl || this.el;
23011     },
23012
23013     // protected
23014     getPositionEl : function(){
23015         return this.positionEl || this.el;
23016     },
23017
23018     /**
23019      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23020      * This method fires the move event.
23021      * @param {Number} left The new left
23022      * @param {Number} top The new top
23023      * @returns {Roo.BoxComponent} this
23024      */
23025     setPosition : function(x, y){
23026         this.x = x;
23027         this.y = y;
23028         if(!this.boxReady){
23029             return this;
23030         }
23031         var adj = this.adjustPosition(x, y);
23032         var ax = adj.x, ay = adj.y;
23033
23034         var el = this.getPositionEl();
23035         if(ax !== undefined || ay !== undefined){
23036             if(ax !== undefined && ay !== undefined){
23037                 el.setLeftTop(ax, ay);
23038             }else if(ax !== undefined){
23039                 el.setLeft(ax);
23040             }else if(ay !== undefined){
23041                 el.setTop(ay);
23042             }
23043             this.onPosition(ax, ay);
23044             this.fireEvent('move', this, ax, ay);
23045         }
23046         return this;
23047     },
23048
23049     /**
23050      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23051      * This method fires the move event.
23052      * @param {Number} x The new x position
23053      * @param {Number} y The new y position
23054      * @returns {Roo.BoxComponent} this
23055      */
23056     setPagePosition : function(x, y){
23057         this.pageX = x;
23058         this.pageY = y;
23059         if(!this.boxReady){
23060             return;
23061         }
23062         if(x === undefined || y === undefined){ // cannot translate undefined points
23063             return;
23064         }
23065         var p = this.el.translatePoints(x, y);
23066         this.setPosition(p.left, p.top);
23067         return this;
23068     },
23069
23070     // private
23071     onRender : function(ct, position){
23072         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23073         if(this.resizeEl){
23074             this.resizeEl = Roo.get(this.resizeEl);
23075         }
23076         if(this.positionEl){
23077             this.positionEl = Roo.get(this.positionEl);
23078         }
23079     },
23080
23081     // private
23082     afterRender : function(){
23083         Roo.BoxComponent.superclass.afterRender.call(this);
23084         this.boxReady = true;
23085         this.setSize(this.width, this.height);
23086         if(this.x || this.y){
23087             this.setPosition(this.x, this.y);
23088         }
23089         if(this.pageX || this.pageY){
23090             this.setPagePosition(this.pageX, this.pageY);
23091         }
23092     },
23093
23094     /**
23095      * Force the component's size to recalculate based on the underlying element's current height and width.
23096      * @returns {Roo.BoxComponent} this
23097      */
23098     syncSize : function(){
23099         delete this.lastSize;
23100         this.setSize(this.el.getWidth(), this.el.getHeight());
23101         return this;
23102     },
23103
23104     /**
23105      * Called after the component is resized, this method is empty by default but can be implemented by any
23106      * subclass that needs to perform custom logic after a resize occurs.
23107      * @param {Number} adjWidth The box-adjusted width that was set
23108      * @param {Number} adjHeight The box-adjusted height that was set
23109      * @param {Number} rawWidth The width that was originally specified
23110      * @param {Number} rawHeight The height that was originally specified
23111      */
23112     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23113
23114     },
23115
23116     /**
23117      * Called after the component is moved, this method is empty by default but can be implemented by any
23118      * subclass that needs to perform custom logic after a move occurs.
23119      * @param {Number} x The new x position
23120      * @param {Number} y The new y position
23121      */
23122     onPosition : function(x, y){
23123
23124     },
23125
23126     // private
23127     adjustSize : function(w, h){
23128         if(this.autoWidth){
23129             w = 'auto';
23130         }
23131         if(this.autoHeight){
23132             h = 'auto';
23133         }
23134         return {width : w, height: h};
23135     },
23136
23137     // private
23138     adjustPosition : function(x, y){
23139         return {x : x, y: y};
23140     }
23141 });/*
23142  * Based on:
23143  * Ext JS Library 1.1.1
23144  * Copyright(c) 2006-2007, Ext JS, LLC.
23145  *
23146  * Originally Released Under LGPL - original licence link has changed is not relivant.
23147  *
23148  * Fork - LGPL
23149  * <script type="text/javascript">
23150  */
23151
23152
23153 /**
23154  * @class Roo.SplitBar
23155  * @extends Roo.util.Observable
23156  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23157  * <br><br>
23158  * Usage:
23159  * <pre><code>
23160 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23161                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23162 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23163 split.minSize = 100;
23164 split.maxSize = 600;
23165 split.animate = true;
23166 split.on('moved', splitterMoved);
23167 </code></pre>
23168  * @constructor
23169  * Create a new SplitBar
23170  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23171  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23172  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23173  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23174                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23175                         position of the SplitBar).
23176  */
23177 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23178     
23179     /** @private */
23180     this.el = Roo.get(dragElement, true);
23181     this.el.dom.unselectable = "on";
23182     /** @private */
23183     this.resizingEl = Roo.get(resizingElement, true);
23184
23185     /**
23186      * @private
23187      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23188      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23189      * @type Number
23190      */
23191     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23192     
23193     /**
23194      * The minimum size of the resizing element. (Defaults to 0)
23195      * @type Number
23196      */
23197     this.minSize = 0;
23198     
23199     /**
23200      * The maximum size of the resizing element. (Defaults to 2000)
23201      * @type Number
23202      */
23203     this.maxSize = 2000;
23204     
23205     /**
23206      * Whether to animate the transition to the new size
23207      * @type Boolean
23208      */
23209     this.animate = false;
23210     
23211     /**
23212      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23213      * @type Boolean
23214      */
23215     this.useShim = false;
23216     
23217     /** @private */
23218     this.shim = null;
23219     
23220     if(!existingProxy){
23221         /** @private */
23222         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23223     }else{
23224         this.proxy = Roo.get(existingProxy).dom;
23225     }
23226     /** @private */
23227     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23228     
23229     /** @private */
23230     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23231     
23232     /** @private */
23233     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23234     
23235     /** @private */
23236     this.dragSpecs = {};
23237     
23238     /**
23239      * @private The adapter to use to positon and resize elements
23240      */
23241     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23242     this.adapter.init(this);
23243     
23244     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23245         /** @private */
23246         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23247         this.el.addClass("x-splitbar-h");
23248     }else{
23249         /** @private */
23250         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23251         this.el.addClass("x-splitbar-v");
23252     }
23253     
23254     this.addEvents({
23255         /**
23256          * @event resize
23257          * Fires when the splitter is moved (alias for {@link #event-moved})
23258          * @param {Roo.SplitBar} this
23259          * @param {Number} newSize the new width or height
23260          */
23261         "resize" : true,
23262         /**
23263          * @event moved
23264          * Fires when the splitter is moved
23265          * @param {Roo.SplitBar} this
23266          * @param {Number} newSize the new width or height
23267          */
23268         "moved" : true,
23269         /**
23270          * @event beforeresize
23271          * Fires before the splitter is dragged
23272          * @param {Roo.SplitBar} this
23273          */
23274         "beforeresize" : true,
23275
23276         "beforeapply" : true
23277     });
23278
23279     Roo.util.Observable.call(this);
23280 };
23281
23282 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23283     onStartProxyDrag : function(x, y){
23284         this.fireEvent("beforeresize", this);
23285         if(!this.overlay){
23286             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23287             o.unselectable();
23288             o.enableDisplayMode("block");
23289             // all splitbars share the same overlay
23290             Roo.SplitBar.prototype.overlay = o;
23291         }
23292         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23293         this.overlay.show();
23294         Roo.get(this.proxy).setDisplayed("block");
23295         var size = this.adapter.getElementSize(this);
23296         this.activeMinSize = this.getMinimumSize();;
23297         this.activeMaxSize = this.getMaximumSize();;
23298         var c1 = size - this.activeMinSize;
23299         var c2 = Math.max(this.activeMaxSize - size, 0);
23300         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23301             this.dd.resetConstraints();
23302             this.dd.setXConstraint(
23303                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23304                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23305             );
23306             this.dd.setYConstraint(0, 0);
23307         }else{
23308             this.dd.resetConstraints();
23309             this.dd.setXConstraint(0, 0);
23310             this.dd.setYConstraint(
23311                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23312                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23313             );
23314          }
23315         this.dragSpecs.startSize = size;
23316         this.dragSpecs.startPoint = [x, y];
23317         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23318     },
23319     
23320     /** 
23321      * @private Called after the drag operation by the DDProxy
23322      */
23323     onEndProxyDrag : function(e){
23324         Roo.get(this.proxy).setDisplayed(false);
23325         var endPoint = Roo.lib.Event.getXY(e);
23326         if(this.overlay){
23327             this.overlay.hide();
23328         }
23329         var newSize;
23330         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23331             newSize = this.dragSpecs.startSize + 
23332                 (this.placement == Roo.SplitBar.LEFT ?
23333                     endPoint[0] - this.dragSpecs.startPoint[0] :
23334                     this.dragSpecs.startPoint[0] - endPoint[0]
23335                 );
23336         }else{
23337             newSize = this.dragSpecs.startSize + 
23338                 (this.placement == Roo.SplitBar.TOP ?
23339                     endPoint[1] - this.dragSpecs.startPoint[1] :
23340                     this.dragSpecs.startPoint[1] - endPoint[1]
23341                 );
23342         }
23343         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23344         if(newSize != this.dragSpecs.startSize){
23345             if(this.fireEvent('beforeapply', this, newSize) !== false){
23346                 this.adapter.setElementSize(this, newSize);
23347                 this.fireEvent("moved", this, newSize);
23348                 this.fireEvent("resize", this, newSize);
23349             }
23350         }
23351     },
23352     
23353     /**
23354      * Get the adapter this SplitBar uses
23355      * @return The adapter object
23356      */
23357     getAdapter : function(){
23358         return this.adapter;
23359     },
23360     
23361     /**
23362      * Set the adapter this SplitBar uses
23363      * @param {Object} adapter A SplitBar adapter object
23364      */
23365     setAdapter : function(adapter){
23366         this.adapter = adapter;
23367         this.adapter.init(this);
23368     },
23369     
23370     /**
23371      * Gets the minimum size for the resizing element
23372      * @return {Number} The minimum size
23373      */
23374     getMinimumSize : function(){
23375         return this.minSize;
23376     },
23377     
23378     /**
23379      * Sets the minimum size for the resizing element
23380      * @param {Number} minSize The minimum size
23381      */
23382     setMinimumSize : function(minSize){
23383         this.minSize = minSize;
23384     },
23385     
23386     /**
23387      * Gets the maximum size for the resizing element
23388      * @return {Number} The maximum size
23389      */
23390     getMaximumSize : function(){
23391         return this.maxSize;
23392     },
23393     
23394     /**
23395      * Sets the maximum size for the resizing element
23396      * @param {Number} maxSize The maximum size
23397      */
23398     setMaximumSize : function(maxSize){
23399         this.maxSize = maxSize;
23400     },
23401     
23402     /**
23403      * Sets the initialize size for the resizing element
23404      * @param {Number} size The initial size
23405      */
23406     setCurrentSize : function(size){
23407         var oldAnimate = this.animate;
23408         this.animate = false;
23409         this.adapter.setElementSize(this, size);
23410         this.animate = oldAnimate;
23411     },
23412     
23413     /**
23414      * Destroy this splitbar. 
23415      * @param {Boolean} removeEl True to remove the element
23416      */
23417     destroy : function(removeEl){
23418         if(this.shim){
23419             this.shim.remove();
23420         }
23421         this.dd.unreg();
23422         this.proxy.parentNode.removeChild(this.proxy);
23423         if(removeEl){
23424             this.el.remove();
23425         }
23426     }
23427 });
23428
23429 /**
23430  * @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.
23431  */
23432 Roo.SplitBar.createProxy = function(dir){
23433     var proxy = new Roo.Element(document.createElement("div"));
23434     proxy.unselectable();
23435     var cls = 'x-splitbar-proxy';
23436     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23437     document.body.appendChild(proxy.dom);
23438     return proxy.dom;
23439 };
23440
23441 /** 
23442  * @class Roo.SplitBar.BasicLayoutAdapter
23443  * Default Adapter. It assumes the splitter and resizing element are not positioned
23444  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23445  */
23446 Roo.SplitBar.BasicLayoutAdapter = function(){
23447 };
23448
23449 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23450     // do nothing for now
23451     init : function(s){
23452     
23453     },
23454     /**
23455      * Called before drag operations to get the current size of the resizing element. 
23456      * @param {Roo.SplitBar} s The SplitBar using this adapter
23457      */
23458      getElementSize : function(s){
23459         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23460             return s.resizingEl.getWidth();
23461         }else{
23462             return s.resizingEl.getHeight();
23463         }
23464     },
23465     
23466     /**
23467      * Called after drag operations to set the size of the resizing element.
23468      * @param {Roo.SplitBar} s The SplitBar using this adapter
23469      * @param {Number} newSize The new size to set
23470      * @param {Function} onComplete A function to be invoked when resizing is complete
23471      */
23472     setElementSize : function(s, newSize, onComplete){
23473         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23474             if(!s.animate){
23475                 s.resizingEl.setWidth(newSize);
23476                 if(onComplete){
23477                     onComplete(s, newSize);
23478                 }
23479             }else{
23480                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23481             }
23482         }else{
23483             
23484             if(!s.animate){
23485                 s.resizingEl.setHeight(newSize);
23486                 if(onComplete){
23487                     onComplete(s, newSize);
23488                 }
23489             }else{
23490                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23491             }
23492         }
23493     }
23494 };
23495
23496 /** 
23497  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23498  * @extends Roo.SplitBar.BasicLayoutAdapter
23499  * Adapter that  moves the splitter element to align with the resized sizing element. 
23500  * Used with an absolute positioned SplitBar.
23501  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23502  * document.body, make sure you assign an id to the body element.
23503  */
23504 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23505     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23506     this.container = Roo.get(container);
23507 };
23508
23509 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23510     init : function(s){
23511         this.basic.init(s);
23512     },
23513     
23514     getElementSize : function(s){
23515         return this.basic.getElementSize(s);
23516     },
23517     
23518     setElementSize : function(s, newSize, onComplete){
23519         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23520     },
23521     
23522     moveSplitter : function(s){
23523         var yes = Roo.SplitBar;
23524         switch(s.placement){
23525             case yes.LEFT:
23526                 s.el.setX(s.resizingEl.getRight());
23527                 break;
23528             case yes.RIGHT:
23529                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23530                 break;
23531             case yes.TOP:
23532                 s.el.setY(s.resizingEl.getBottom());
23533                 break;
23534             case yes.BOTTOM:
23535                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23536                 break;
23537         }
23538     }
23539 };
23540
23541 /**
23542  * Orientation constant - Create a vertical SplitBar
23543  * @static
23544  * @type Number
23545  */
23546 Roo.SplitBar.VERTICAL = 1;
23547
23548 /**
23549  * Orientation constant - Create a horizontal SplitBar
23550  * @static
23551  * @type Number
23552  */
23553 Roo.SplitBar.HORIZONTAL = 2;
23554
23555 /**
23556  * Placement constant - The resizing element is to the left of the splitter element
23557  * @static
23558  * @type Number
23559  */
23560 Roo.SplitBar.LEFT = 1;
23561
23562 /**
23563  * Placement constant - The resizing element is to the right of the splitter element
23564  * @static
23565  * @type Number
23566  */
23567 Roo.SplitBar.RIGHT = 2;
23568
23569 /**
23570  * Placement constant - The resizing element is positioned above the splitter element
23571  * @static
23572  * @type Number
23573  */
23574 Roo.SplitBar.TOP = 3;
23575
23576 /**
23577  * Placement constant - The resizing element is positioned under splitter element
23578  * @static
23579  * @type Number
23580  */
23581 Roo.SplitBar.BOTTOM = 4;
23582 /*
23583  * Based on:
23584  * Ext JS Library 1.1.1
23585  * Copyright(c) 2006-2007, Ext JS, LLC.
23586  *
23587  * Originally Released Under LGPL - original licence link has changed is not relivant.
23588  *
23589  * Fork - LGPL
23590  * <script type="text/javascript">
23591  */
23592
23593 /**
23594  * @class Roo.View
23595  * @extends Roo.util.Observable
23596  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23597  * This class also supports single and multi selection modes. <br>
23598  * Create a data model bound view:
23599  <pre><code>
23600  var store = new Roo.data.Store(...);
23601
23602  var view = new Roo.View({
23603     el : "my-element",
23604     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23605  
23606     singleSelect: true,
23607     selectedClass: "ydataview-selected",
23608     store: store
23609  });
23610
23611  // listen for node click?
23612  view.on("click", function(vw, index, node, e){
23613  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23614  });
23615
23616  // load XML data
23617  dataModel.load("foobar.xml");
23618  </code></pre>
23619  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23620  * <br><br>
23621  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23622  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23623  * 
23624  * Note: old style constructor is still suported (container, template, config)
23625  * 
23626  * @constructor
23627  * Create a new View
23628  * @param {Object} config The config object
23629  * 
23630  */
23631 Roo.View = function(config, depreciated_tpl, depreciated_config){
23632     
23633     if (typeof(depreciated_tpl) == 'undefined') {
23634         // new way.. - universal constructor.
23635         Roo.apply(this, config);
23636         this.el  = Roo.get(this.el);
23637     } else {
23638         // old format..
23639         this.el  = Roo.get(config);
23640         this.tpl = depreciated_tpl;
23641         Roo.apply(this, depreciated_config);
23642     }
23643      
23644     
23645     if(typeof(this.tpl) == "string"){
23646         this.tpl = new Roo.Template(this.tpl);
23647     } else {
23648         // support xtype ctors..
23649         this.tpl = new Roo.factory(this.tpl, Roo);
23650     }
23651     
23652     
23653     this.tpl.compile();
23654    
23655
23656      
23657     /** @private */
23658     this.addEvents({
23659         /**
23660          * @event beforeclick
23661          * Fires before a click is processed. Returns false to cancel the default action.
23662          * @param {Roo.View} this
23663          * @param {Number} index The index of the target node
23664          * @param {HTMLElement} node The target node
23665          * @param {Roo.EventObject} e The raw event object
23666          */
23667             "beforeclick" : true,
23668         /**
23669          * @event click
23670          * Fires when a template node is clicked.
23671          * @param {Roo.View} this
23672          * @param {Number} index The index of the target node
23673          * @param {HTMLElement} node The target node
23674          * @param {Roo.EventObject} e The raw event object
23675          */
23676             "click" : true,
23677         /**
23678          * @event dblclick
23679          * Fires when a template node is double clicked.
23680          * @param {Roo.View} this
23681          * @param {Number} index The index of the target node
23682          * @param {HTMLElement} node The target node
23683          * @param {Roo.EventObject} e The raw event object
23684          */
23685             "dblclick" : true,
23686         /**
23687          * @event contextmenu
23688          * Fires when a template node is right clicked.
23689          * @param {Roo.View} this
23690          * @param {Number} index The index of the target node
23691          * @param {HTMLElement} node The target node
23692          * @param {Roo.EventObject} e The raw event object
23693          */
23694             "contextmenu" : true,
23695         /**
23696          * @event selectionchange
23697          * Fires when the selected nodes change.
23698          * @param {Roo.View} this
23699          * @param {Array} selections Array of the selected nodes
23700          */
23701             "selectionchange" : true,
23702     
23703         /**
23704          * @event beforeselect
23705          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23706          * @param {Roo.View} this
23707          * @param {HTMLElement} node The node to be selected
23708          * @param {Array} selections Array of currently selected nodes
23709          */
23710             "beforeselect" : true,
23711         /**
23712          * @event preparedata
23713          * Fires on every row to render, to allow you to change the data.
23714          * @param {Roo.View} this
23715          * @param {Object} data to be rendered (change this)
23716          */
23717           "preparedata" : true
23718         });
23719
23720     this.el.on({
23721         "click": this.onClick,
23722         "dblclick": this.onDblClick,
23723         "contextmenu": this.onContextMenu,
23724         scope:this
23725     });
23726
23727     this.selections = [];
23728     this.nodes = [];
23729     this.cmp = new Roo.CompositeElementLite([]);
23730     if(this.store){
23731         this.store = Roo.factory(this.store, Roo.data);
23732         this.setStore(this.store, true);
23733     }
23734     Roo.View.superclass.constructor.call(this);
23735 };
23736
23737 Roo.extend(Roo.View, Roo.util.Observable, {
23738     
23739      /**
23740      * @cfg {Roo.data.Store} store Data store to load data from.
23741      */
23742     store : false,
23743     
23744     /**
23745      * @cfg {String|Roo.Element} el The container element.
23746      */
23747     el : '',
23748     
23749     /**
23750      * @cfg {String|Roo.Template} tpl The template used by this View 
23751      */
23752     tpl : false,
23753     
23754     /**
23755      * @cfg {String} selectedClass The css class to add to selected nodes
23756      */
23757     selectedClass : "x-view-selected",
23758      /**
23759      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23760      */
23761     emptyText : "",
23762     /**
23763      * @cfg {Boolean} multiSelect Allow multiple selection
23764      */
23765     multiSelect : false,
23766     /**
23767      * @cfg {Boolean} singleSelect Allow single selection
23768      */
23769     singleSelect:  false,
23770     
23771     /**
23772      * @cfg {Boolean} toggleSelect - selecting 
23773      */
23774     toggleSelect : false,
23775     
23776     /**
23777      * Returns the element this view is bound to.
23778      * @return {Roo.Element}
23779      */
23780     getEl : function(){
23781         return this.el;
23782     },
23783
23784     /**
23785      * Refreshes the view.
23786      */
23787     refresh : function(){
23788         var t = this.tpl;
23789         this.clearSelections();
23790         this.el.update("");
23791         var html = [];
23792         var records = this.store.getRange();
23793         if(records.length < 1){
23794             this.el.update(this.emptyText);
23795             return;
23796         }
23797         for(var i = 0, len = records.length; i < len; i++){
23798             var data = this.prepareData(records[i].data, i, records[i]);
23799             this.fireEvent("preparedata", this, data, i, records[i]);
23800             html[html.length] = t.apply(data);
23801         }
23802         this.el.update(html.join(""));
23803         this.nodes = this.el.dom.childNodes;
23804         this.updateIndexes(0);
23805     },
23806
23807     /**
23808      * Function to override to reformat the data that is sent to
23809      * the template for each node.
23810      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23811      * a JSON object for an UpdateManager bound view).
23812      */
23813     prepareData : function(data){
23814         return data;
23815     },
23816
23817     onUpdate : function(ds, record){
23818         this.clearSelections();
23819         var index = this.store.indexOf(record);
23820         var n = this.nodes[index];
23821         this.tpl.insertBefore(n, this.prepareData(record.data));
23822         n.parentNode.removeChild(n);
23823         this.updateIndexes(index, index);
23824     },
23825
23826     onAdd : function(ds, records, index){
23827         this.clearSelections();
23828         if(this.nodes.length == 0){
23829             this.refresh();
23830             return;
23831         }
23832         var n = this.nodes[index];
23833         for(var i = 0, len = records.length; i < len; i++){
23834             var d = this.prepareData(records[i].data);
23835             if(n){
23836                 this.tpl.insertBefore(n, d);
23837             }else{
23838                 this.tpl.append(this.el, d);
23839             }
23840         }
23841         this.updateIndexes(index);
23842     },
23843
23844     onRemove : function(ds, record, index){
23845         this.clearSelections();
23846         this.el.dom.removeChild(this.nodes[index]);
23847         this.updateIndexes(index);
23848     },
23849
23850     /**
23851      * Refresh an individual node.
23852      * @param {Number} index
23853      */
23854     refreshNode : function(index){
23855         this.onUpdate(this.store, this.store.getAt(index));
23856     },
23857
23858     updateIndexes : function(startIndex, endIndex){
23859         var ns = this.nodes;
23860         startIndex = startIndex || 0;
23861         endIndex = endIndex || ns.length - 1;
23862         for(var i = startIndex; i <= endIndex; i++){
23863             ns[i].nodeIndex = i;
23864         }
23865     },
23866
23867     /**
23868      * Changes the data store this view uses and refresh the view.
23869      * @param {Store} store
23870      */
23871     setStore : function(store, initial){
23872         if(!initial && this.store){
23873             this.store.un("datachanged", this.refresh);
23874             this.store.un("add", this.onAdd);
23875             this.store.un("remove", this.onRemove);
23876             this.store.un("update", this.onUpdate);
23877             this.store.un("clear", this.refresh);
23878         }
23879         if(store){
23880           
23881             store.on("datachanged", this.refresh, this);
23882             store.on("add", this.onAdd, this);
23883             store.on("remove", this.onRemove, this);
23884             store.on("update", this.onUpdate, this);
23885             store.on("clear", this.refresh, this);
23886         }
23887         
23888         if(store){
23889             this.refresh();
23890         }
23891     },
23892
23893     /**
23894      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23895      * @param {HTMLElement} node
23896      * @return {HTMLElement} The template node
23897      */
23898     findItemFromChild : function(node){
23899         var el = this.el.dom;
23900         if(!node || node.parentNode == el){
23901                     return node;
23902             }
23903             var p = node.parentNode;
23904             while(p && p != el){
23905             if(p.parentNode == el){
23906                 return p;
23907             }
23908             p = p.parentNode;
23909         }
23910             return null;
23911     },
23912
23913     /** @ignore */
23914     onClick : function(e){
23915         var item = this.findItemFromChild(e.getTarget());
23916         if(item){
23917             var index = this.indexOf(item);
23918             if(this.onItemClick(item, index, e) !== false){
23919                 this.fireEvent("click", this, index, item, e);
23920             }
23921         }else{
23922             this.clearSelections();
23923         }
23924     },
23925
23926     /** @ignore */
23927     onContextMenu : function(e){
23928         var item = this.findItemFromChild(e.getTarget());
23929         if(item){
23930             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23931         }
23932     },
23933
23934     /** @ignore */
23935     onDblClick : function(e){
23936         var item = this.findItemFromChild(e.getTarget());
23937         if(item){
23938             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23939         }
23940     },
23941
23942     onItemClick : function(item, index, e)
23943     {
23944         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23945             return false;
23946         }
23947         if (this.toggleSelect) {
23948             var m = this.isSelected(item) ? 'unselect' : 'select';
23949             Roo.log(m);
23950             var _t = this;
23951             _t[m](item, true, false);
23952             return true;
23953         }
23954         if(this.multiSelect || this.singleSelect){
23955             if(this.multiSelect && e.shiftKey && this.lastSelection){
23956                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23957             }else{
23958                 this.select(item, this.multiSelect && e.ctrlKey);
23959                 this.lastSelection = item;
23960             }
23961             e.preventDefault();
23962         }
23963         return true;
23964     },
23965
23966     /**
23967      * Get the number of selected nodes.
23968      * @return {Number}
23969      */
23970     getSelectionCount : function(){
23971         return this.selections.length;
23972     },
23973
23974     /**
23975      * Get the currently selected nodes.
23976      * @return {Array} An array of HTMLElements
23977      */
23978     getSelectedNodes : function(){
23979         return this.selections;
23980     },
23981
23982     /**
23983      * Get the indexes of the selected nodes.
23984      * @return {Array}
23985      */
23986     getSelectedIndexes : function(){
23987         var indexes = [], s = this.selections;
23988         for(var i = 0, len = s.length; i < len; i++){
23989             indexes.push(s[i].nodeIndex);
23990         }
23991         return indexes;
23992     },
23993
23994     /**
23995      * Clear all selections
23996      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23997      */
23998     clearSelections : function(suppressEvent){
23999         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
24000             this.cmp.elements = this.selections;
24001             this.cmp.removeClass(this.selectedClass);
24002             this.selections = [];
24003             if(!suppressEvent){
24004                 this.fireEvent("selectionchange", this, this.selections);
24005             }
24006         }
24007     },
24008
24009     /**
24010      * Returns true if the passed node is selected
24011      * @param {HTMLElement/Number} node The node or node index
24012      * @return {Boolean}
24013      */
24014     isSelected : function(node){
24015         var s = this.selections;
24016         if(s.length < 1){
24017             return false;
24018         }
24019         node = this.getNode(node);
24020         return s.indexOf(node) !== -1;
24021     },
24022
24023     /**
24024      * Selects nodes.
24025      * @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
24026      * @param {Boolean} keepExisting (optional) true to keep existing selections
24027      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24028      */
24029     select : function(nodeInfo, keepExisting, suppressEvent){
24030         if(nodeInfo instanceof Array){
24031             if(!keepExisting){
24032                 this.clearSelections(true);
24033             }
24034             for(var i = 0, len = nodeInfo.length; i < len; i++){
24035                 this.select(nodeInfo[i], true, true);
24036             }
24037             return;
24038         } 
24039         var node = this.getNode(nodeInfo);
24040         if(!node || this.isSelected(node)){
24041             return; // already selected.
24042         }
24043         if(!keepExisting){
24044             this.clearSelections(true);
24045         }
24046         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24047             Roo.fly(node).addClass(this.selectedClass);
24048             this.selections.push(node);
24049             if(!suppressEvent){
24050                 this.fireEvent("selectionchange", this, this.selections);
24051             }
24052         }
24053         
24054         
24055     },
24056       /**
24057      * Unselects nodes.
24058      * @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
24059      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24060      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24061      */
24062     unselect : function(nodeInfo, keepExisting, suppressEvent)
24063     {
24064         if(nodeInfo instanceof Array){
24065             Roo.each(this.selections, function(s) {
24066                 this.unselect(s, nodeInfo);
24067             }, this);
24068             return;
24069         }
24070         var node = this.getNode(nodeInfo);
24071         if(!node || !this.isSelected(node)){
24072             Roo.log("not selected");
24073             return; // not selected.
24074         }
24075         // fireevent???
24076         var ns = [];
24077         Roo.each(this.selections, function(s) {
24078             if (s == node ) {
24079                 Roo.fly(node).removeClass(this.selectedClass);
24080
24081                 return;
24082             }
24083             ns.push(s);
24084         },this);
24085         
24086         this.selections= ns;
24087         this.fireEvent("selectionchange", this, this.selections);
24088     },
24089
24090     /**
24091      * Gets a template node.
24092      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24093      * @return {HTMLElement} The node or null if it wasn't found
24094      */
24095     getNode : function(nodeInfo){
24096         if(typeof nodeInfo == "string"){
24097             return document.getElementById(nodeInfo);
24098         }else if(typeof nodeInfo == "number"){
24099             return this.nodes[nodeInfo];
24100         }
24101         return nodeInfo;
24102     },
24103
24104     /**
24105      * Gets a range template nodes.
24106      * @param {Number} startIndex
24107      * @param {Number} endIndex
24108      * @return {Array} An array of nodes
24109      */
24110     getNodes : function(start, end){
24111         var ns = this.nodes;
24112         start = start || 0;
24113         end = typeof end == "undefined" ? ns.length - 1 : end;
24114         var nodes = [];
24115         if(start <= end){
24116             for(var i = start; i <= end; i++){
24117                 nodes.push(ns[i]);
24118             }
24119         } else{
24120             for(var i = start; i >= end; i--){
24121                 nodes.push(ns[i]);
24122             }
24123         }
24124         return nodes;
24125     },
24126
24127     /**
24128      * Finds the index of the passed node
24129      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24130      * @return {Number} The index of the node or -1
24131      */
24132     indexOf : function(node){
24133         node = this.getNode(node);
24134         if(typeof node.nodeIndex == "number"){
24135             return node.nodeIndex;
24136         }
24137         var ns = this.nodes;
24138         for(var i = 0, len = ns.length; i < len; i++){
24139             if(ns[i] == node){
24140                 return i;
24141             }
24142         }
24143         return -1;
24144     }
24145 });
24146 /*
24147  * Based on:
24148  * Ext JS Library 1.1.1
24149  * Copyright(c) 2006-2007, Ext JS, LLC.
24150  *
24151  * Originally Released Under LGPL - original licence link has changed is not relivant.
24152  *
24153  * Fork - LGPL
24154  * <script type="text/javascript">
24155  */
24156
24157 /**
24158  * @class Roo.JsonView
24159  * @extends Roo.View
24160  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24161 <pre><code>
24162 var view = new Roo.JsonView({
24163     container: "my-element",
24164     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24165     multiSelect: true, 
24166     jsonRoot: "data" 
24167 });
24168
24169 // listen for node click?
24170 view.on("click", function(vw, index, node, e){
24171     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24172 });
24173
24174 // direct load of JSON data
24175 view.load("foobar.php");
24176
24177 // Example from my blog list
24178 var tpl = new Roo.Template(
24179     '&lt;div class="entry"&gt;' +
24180     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24181     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24182     "&lt;/div&gt;&lt;hr /&gt;"
24183 );
24184
24185 var moreView = new Roo.JsonView({
24186     container :  "entry-list", 
24187     template : tpl,
24188     jsonRoot: "posts"
24189 });
24190 moreView.on("beforerender", this.sortEntries, this);
24191 moreView.load({
24192     url: "/blog/get-posts.php",
24193     params: "allposts=true",
24194     text: "Loading Blog Entries..."
24195 });
24196 </code></pre>
24197
24198 * Note: old code is supported with arguments : (container, template, config)
24199
24200
24201  * @constructor
24202  * Create a new JsonView
24203  * 
24204  * @param {Object} config The config object
24205  * 
24206  */
24207 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24208     
24209     
24210     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24211
24212     var um = this.el.getUpdateManager();
24213     um.setRenderer(this);
24214     um.on("update", this.onLoad, this);
24215     um.on("failure", this.onLoadException, this);
24216
24217     /**
24218      * @event beforerender
24219      * Fires before rendering of the downloaded JSON data.
24220      * @param {Roo.JsonView} this
24221      * @param {Object} data The JSON data loaded
24222      */
24223     /**
24224      * @event load
24225      * Fires when data is loaded.
24226      * @param {Roo.JsonView} this
24227      * @param {Object} data The JSON data loaded
24228      * @param {Object} response The raw Connect response object
24229      */
24230     /**
24231      * @event loadexception
24232      * Fires when loading fails.
24233      * @param {Roo.JsonView} this
24234      * @param {Object} response The raw Connect response object
24235      */
24236     this.addEvents({
24237         'beforerender' : true,
24238         'load' : true,
24239         'loadexception' : true
24240     });
24241 };
24242 Roo.extend(Roo.JsonView, Roo.View, {
24243     /**
24244      * @type {String} The root property in the loaded JSON object that contains the data
24245      */
24246     jsonRoot : "",
24247
24248     /**
24249      * Refreshes the view.
24250      */
24251     refresh : function(){
24252         this.clearSelections();
24253         this.el.update("");
24254         var html = [];
24255         var o = this.jsonData;
24256         if(o && o.length > 0){
24257             for(var i = 0, len = o.length; i < len; i++){
24258                 var data = this.prepareData(o[i], i, o);
24259                 html[html.length] = this.tpl.apply(data);
24260             }
24261         }else{
24262             html.push(this.emptyText);
24263         }
24264         this.el.update(html.join(""));
24265         this.nodes = this.el.dom.childNodes;
24266         this.updateIndexes(0);
24267     },
24268
24269     /**
24270      * 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.
24271      * @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:
24272      <pre><code>
24273      view.load({
24274          url: "your-url.php",
24275          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24276          callback: yourFunction,
24277          scope: yourObject, //(optional scope)
24278          discardUrl: false,
24279          nocache: false,
24280          text: "Loading...",
24281          timeout: 30,
24282          scripts: false
24283      });
24284      </code></pre>
24285      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24286      * 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.
24287      * @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}
24288      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24289      * @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.
24290      */
24291     load : function(){
24292         var um = this.el.getUpdateManager();
24293         um.update.apply(um, arguments);
24294     },
24295
24296     render : function(el, response){
24297         this.clearSelections();
24298         this.el.update("");
24299         var o;
24300         try{
24301             o = Roo.util.JSON.decode(response.responseText);
24302             if(this.jsonRoot){
24303                 
24304                 o = o[this.jsonRoot];
24305             }
24306         } catch(e){
24307         }
24308         /**
24309          * The current JSON data or null
24310          */
24311         this.jsonData = o;
24312         this.beforeRender();
24313         this.refresh();
24314     },
24315
24316 /**
24317  * Get the number of records in the current JSON dataset
24318  * @return {Number}
24319  */
24320     getCount : function(){
24321         return this.jsonData ? this.jsonData.length : 0;
24322     },
24323
24324 /**
24325  * Returns the JSON object for the specified node(s)
24326  * @param {HTMLElement/Array} node The node or an array of nodes
24327  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24328  * you get the JSON object for the node
24329  */
24330     getNodeData : function(node){
24331         if(node instanceof Array){
24332             var data = [];
24333             for(var i = 0, len = node.length; i < len; i++){
24334                 data.push(this.getNodeData(node[i]));
24335             }
24336             return data;
24337         }
24338         return this.jsonData[this.indexOf(node)] || null;
24339     },
24340
24341     beforeRender : function(){
24342         this.snapshot = this.jsonData;
24343         if(this.sortInfo){
24344             this.sort.apply(this, this.sortInfo);
24345         }
24346         this.fireEvent("beforerender", this, this.jsonData);
24347     },
24348
24349     onLoad : function(el, o){
24350         this.fireEvent("load", this, this.jsonData, o);
24351     },
24352
24353     onLoadException : function(el, o){
24354         this.fireEvent("loadexception", this, o);
24355     },
24356
24357 /**
24358  * Filter the data by a specific property.
24359  * @param {String} property A property on your JSON objects
24360  * @param {String/RegExp} value Either string that the property values
24361  * should start with, or a RegExp to test against the property
24362  */
24363     filter : function(property, value){
24364         if(this.jsonData){
24365             var data = [];
24366             var ss = this.snapshot;
24367             if(typeof value == "string"){
24368                 var vlen = value.length;
24369                 if(vlen == 0){
24370                     this.clearFilter();
24371                     return;
24372                 }
24373                 value = value.toLowerCase();
24374                 for(var i = 0, len = ss.length; i < len; i++){
24375                     var o = ss[i];
24376                     if(o[property].substr(0, vlen).toLowerCase() == value){
24377                         data.push(o);
24378                     }
24379                 }
24380             } else if(value.exec){ // regex?
24381                 for(var i = 0, len = ss.length; i < len; i++){
24382                     var o = ss[i];
24383                     if(value.test(o[property])){
24384                         data.push(o);
24385                     }
24386                 }
24387             } else{
24388                 return;
24389             }
24390             this.jsonData = data;
24391             this.refresh();
24392         }
24393     },
24394
24395 /**
24396  * Filter by a function. The passed function will be called with each
24397  * object in the current dataset. If the function returns true the value is kept,
24398  * otherwise it is filtered.
24399  * @param {Function} fn
24400  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24401  */
24402     filterBy : function(fn, scope){
24403         if(this.jsonData){
24404             var data = [];
24405             var ss = this.snapshot;
24406             for(var i = 0, len = ss.length; i < len; i++){
24407                 var o = ss[i];
24408                 if(fn.call(scope || this, o)){
24409                     data.push(o);
24410                 }
24411             }
24412             this.jsonData = data;
24413             this.refresh();
24414         }
24415     },
24416
24417 /**
24418  * Clears the current filter.
24419  */
24420     clearFilter : function(){
24421         if(this.snapshot && this.jsonData != this.snapshot){
24422             this.jsonData = this.snapshot;
24423             this.refresh();
24424         }
24425     },
24426
24427
24428 /**
24429  * Sorts the data for this view and refreshes it.
24430  * @param {String} property A property on your JSON objects to sort on
24431  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24432  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24433  */
24434     sort : function(property, dir, sortType){
24435         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24436         if(this.jsonData){
24437             var p = property;
24438             var dsc = dir && dir.toLowerCase() == "desc";
24439             var f = function(o1, o2){
24440                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24441                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24442                 ;
24443                 if(v1 < v2){
24444                     return dsc ? +1 : -1;
24445                 } else if(v1 > v2){
24446                     return dsc ? -1 : +1;
24447                 } else{
24448                     return 0;
24449                 }
24450             };
24451             this.jsonData.sort(f);
24452             this.refresh();
24453             if(this.jsonData != this.snapshot){
24454                 this.snapshot.sort(f);
24455             }
24456         }
24457     }
24458 });/*
24459  * Based on:
24460  * Ext JS Library 1.1.1
24461  * Copyright(c) 2006-2007, Ext JS, LLC.
24462  *
24463  * Originally Released Under LGPL - original licence link has changed is not relivant.
24464  *
24465  * Fork - LGPL
24466  * <script type="text/javascript">
24467  */
24468  
24469
24470 /**
24471  * @class Roo.ColorPalette
24472  * @extends Roo.Component
24473  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24474  * Here's an example of typical usage:
24475  * <pre><code>
24476 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24477 cp.render('my-div');
24478
24479 cp.on('select', function(palette, selColor){
24480     // do something with selColor
24481 });
24482 </code></pre>
24483  * @constructor
24484  * Create a new ColorPalette
24485  * @param {Object} config The config object
24486  */
24487 Roo.ColorPalette = function(config){
24488     Roo.ColorPalette.superclass.constructor.call(this, config);
24489     this.addEvents({
24490         /**
24491              * @event select
24492              * Fires when a color is selected
24493              * @param {ColorPalette} this
24494              * @param {String} color The 6-digit color hex code (without the # symbol)
24495              */
24496         select: true
24497     });
24498
24499     if(this.handler){
24500         this.on("select", this.handler, this.scope, true);
24501     }
24502 };
24503 Roo.extend(Roo.ColorPalette, Roo.Component, {
24504     /**
24505      * @cfg {String} itemCls
24506      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24507      */
24508     itemCls : "x-color-palette",
24509     /**
24510      * @cfg {String} value
24511      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24512      * the hex codes are case-sensitive.
24513      */
24514     value : null,
24515     clickEvent:'click',
24516     // private
24517     ctype: "Roo.ColorPalette",
24518
24519     /**
24520      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24521      */
24522     allowReselect : false,
24523
24524     /**
24525      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24526      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24527      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24528      * of colors with the width setting until the box is symmetrical.</p>
24529      * <p>You can override individual colors if needed:</p>
24530      * <pre><code>
24531 var cp = new Roo.ColorPalette();
24532 cp.colors[0] = "FF0000";  // change the first box to red
24533 </code></pre>
24534
24535 Or you can provide a custom array of your own for complete control:
24536 <pre><code>
24537 var cp = new Roo.ColorPalette();
24538 cp.colors = ["000000", "993300", "333300"];
24539 </code></pre>
24540      * @type Array
24541      */
24542     colors : [
24543         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24544         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24545         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24546         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24547         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24548     ],
24549
24550     // private
24551     onRender : function(container, position){
24552         var t = new Roo.MasterTemplate(
24553             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24554         );
24555         var c = this.colors;
24556         for(var i = 0, len = c.length; i < len; i++){
24557             t.add([c[i]]);
24558         }
24559         var el = document.createElement("div");
24560         el.className = this.itemCls;
24561         t.overwrite(el);
24562         container.dom.insertBefore(el, position);
24563         this.el = Roo.get(el);
24564         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24565         if(this.clickEvent != 'click'){
24566             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24567         }
24568     },
24569
24570     // private
24571     afterRender : function(){
24572         Roo.ColorPalette.superclass.afterRender.call(this);
24573         if(this.value){
24574             var s = this.value;
24575             this.value = null;
24576             this.select(s);
24577         }
24578     },
24579
24580     // private
24581     handleClick : function(e, t){
24582         e.preventDefault();
24583         if(!this.disabled){
24584             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24585             this.select(c.toUpperCase());
24586         }
24587     },
24588
24589     /**
24590      * Selects the specified color in the palette (fires the select event)
24591      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24592      */
24593     select : function(color){
24594         color = color.replace("#", "");
24595         if(color != this.value || this.allowReselect){
24596             var el = this.el;
24597             if(this.value){
24598                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24599             }
24600             el.child("a.color-"+color).addClass("x-color-palette-sel");
24601             this.value = color;
24602             this.fireEvent("select", this, color);
24603         }
24604     }
24605 });/*
24606  * Based on:
24607  * Ext JS Library 1.1.1
24608  * Copyright(c) 2006-2007, Ext JS, LLC.
24609  *
24610  * Originally Released Under LGPL - original licence link has changed is not relivant.
24611  *
24612  * Fork - LGPL
24613  * <script type="text/javascript">
24614  */
24615  
24616 /**
24617  * @class Roo.DatePicker
24618  * @extends Roo.Component
24619  * Simple date picker class.
24620  * @constructor
24621  * Create a new DatePicker
24622  * @param {Object} config The config object
24623  */
24624 Roo.DatePicker = function(config){
24625     Roo.DatePicker.superclass.constructor.call(this, config);
24626
24627     this.value = config && config.value ?
24628                  config.value.clearTime() : new Date().clearTime();
24629
24630     this.addEvents({
24631         /**
24632              * @event select
24633              * Fires when a date is selected
24634              * @param {DatePicker} this
24635              * @param {Date} date The selected date
24636              */
24637         'select': true,
24638         /**
24639              * @event monthchange
24640              * Fires when the displayed month changes 
24641              * @param {DatePicker} this
24642              * @param {Date} date The selected month
24643              */
24644         'monthchange': true
24645     });
24646
24647     if(this.handler){
24648         this.on("select", this.handler,  this.scope || this);
24649     }
24650     // build the disabledDatesRE
24651     if(!this.disabledDatesRE && this.disabledDates){
24652         var dd = this.disabledDates;
24653         var re = "(?:";
24654         for(var i = 0; i < dd.length; i++){
24655             re += dd[i];
24656             if(i != dd.length-1) re += "|";
24657         }
24658         this.disabledDatesRE = new RegExp(re + ")");
24659     }
24660 };
24661
24662 Roo.extend(Roo.DatePicker, Roo.Component, {
24663     /**
24664      * @cfg {String} todayText
24665      * The text to display on the button that selects the current date (defaults to "Today")
24666      */
24667     todayText : "Today",
24668     /**
24669      * @cfg {String} okText
24670      * The text to display on the ok button
24671      */
24672     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24673     /**
24674      * @cfg {String} cancelText
24675      * The text to display on the cancel button
24676      */
24677     cancelText : "Cancel",
24678     /**
24679      * @cfg {String} todayTip
24680      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24681      */
24682     todayTip : "{0} (Spacebar)",
24683     /**
24684      * @cfg {Date} minDate
24685      * Minimum allowable date (JavaScript date object, defaults to null)
24686      */
24687     minDate : null,
24688     /**
24689      * @cfg {Date} maxDate
24690      * Maximum allowable date (JavaScript date object, defaults to null)
24691      */
24692     maxDate : null,
24693     /**
24694      * @cfg {String} minText
24695      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24696      */
24697     minText : "This date is before the minimum date",
24698     /**
24699      * @cfg {String} maxText
24700      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24701      */
24702     maxText : "This date is after the maximum date",
24703     /**
24704      * @cfg {String} format
24705      * The default date format string which can be overriden for localization support.  The format must be
24706      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24707      */
24708     format : "m/d/y",
24709     /**
24710      * @cfg {Array} disabledDays
24711      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24712      */
24713     disabledDays : null,
24714     /**
24715      * @cfg {String} disabledDaysText
24716      * The tooltip to display when the date falls on a disabled day (defaults to "")
24717      */
24718     disabledDaysText : "",
24719     /**
24720      * @cfg {RegExp} disabledDatesRE
24721      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24722      */
24723     disabledDatesRE : null,
24724     /**
24725      * @cfg {String} disabledDatesText
24726      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24727      */
24728     disabledDatesText : "",
24729     /**
24730      * @cfg {Boolean} constrainToViewport
24731      * True to constrain the date picker to the viewport (defaults to true)
24732      */
24733     constrainToViewport : true,
24734     /**
24735      * @cfg {Array} monthNames
24736      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24737      */
24738     monthNames : Date.monthNames,
24739     /**
24740      * @cfg {Array} dayNames
24741      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24742      */
24743     dayNames : Date.dayNames,
24744     /**
24745      * @cfg {String} nextText
24746      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24747      */
24748     nextText: 'Next Month (Control+Right)',
24749     /**
24750      * @cfg {String} prevText
24751      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24752      */
24753     prevText: 'Previous Month (Control+Left)',
24754     /**
24755      * @cfg {String} monthYearText
24756      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24757      */
24758     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24759     /**
24760      * @cfg {Number} startDay
24761      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24762      */
24763     startDay : 0,
24764     /**
24765      * @cfg {Bool} showClear
24766      * Show a clear button (usefull for date form elements that can be blank.)
24767      */
24768     
24769     showClear: false,
24770     
24771     /**
24772      * Sets the value of the date field
24773      * @param {Date} value The date to set
24774      */
24775     setValue : function(value){
24776         var old = this.value;
24777         this.value = value.clearTime(true);
24778         if(this.el){
24779             this.update(this.value);
24780         }
24781     },
24782
24783     /**
24784      * Gets the current selected value of the date field
24785      * @return {Date} The selected date
24786      */
24787     getValue : function(){
24788         return this.value;
24789     },
24790
24791     // private
24792     focus : function(){
24793         if(this.el){
24794             this.update(this.activeDate);
24795         }
24796     },
24797
24798     // private
24799     onRender : function(container, position){
24800         var m = [
24801              '<table cellspacing="0">',
24802                 '<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>',
24803                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24804         var dn = this.dayNames;
24805         for(var i = 0; i < 7; i++){
24806             var d = this.startDay+i;
24807             if(d > 6){
24808                 d = d-7;
24809             }
24810             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24811         }
24812         m[m.length] = "</tr></thead><tbody><tr>";
24813         for(var i = 0; i < 42; i++) {
24814             if(i % 7 == 0 && i != 0){
24815                 m[m.length] = "</tr><tr>";
24816             }
24817             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24818         }
24819         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24820             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24821
24822         var el = document.createElement("div");
24823         el.className = "x-date-picker";
24824         el.innerHTML = m.join("");
24825
24826         container.dom.insertBefore(el, position);
24827
24828         this.el = Roo.get(el);
24829         this.eventEl = Roo.get(el.firstChild);
24830
24831         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24832             handler: this.showPrevMonth,
24833             scope: this,
24834             preventDefault:true,
24835             stopDefault:true
24836         });
24837
24838         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24839             handler: this.showNextMonth,
24840             scope: this,
24841             preventDefault:true,
24842             stopDefault:true
24843         });
24844
24845         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24846
24847         this.monthPicker = this.el.down('div.x-date-mp');
24848         this.monthPicker.enableDisplayMode('block');
24849         
24850         var kn = new Roo.KeyNav(this.eventEl, {
24851             "left" : function(e){
24852                 e.ctrlKey ?
24853                     this.showPrevMonth() :
24854                     this.update(this.activeDate.add("d", -1));
24855             },
24856
24857             "right" : function(e){
24858                 e.ctrlKey ?
24859                     this.showNextMonth() :
24860                     this.update(this.activeDate.add("d", 1));
24861             },
24862
24863             "up" : function(e){
24864                 e.ctrlKey ?
24865                     this.showNextYear() :
24866                     this.update(this.activeDate.add("d", -7));
24867             },
24868
24869             "down" : function(e){
24870                 e.ctrlKey ?
24871                     this.showPrevYear() :
24872                     this.update(this.activeDate.add("d", 7));
24873             },
24874
24875             "pageUp" : function(e){
24876                 this.showNextMonth();
24877             },
24878
24879             "pageDown" : function(e){
24880                 this.showPrevMonth();
24881             },
24882
24883             "enter" : function(e){
24884                 e.stopPropagation();
24885                 return true;
24886             },
24887
24888             scope : this
24889         });
24890
24891         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24892
24893         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24894
24895         this.el.unselectable();
24896         
24897         this.cells = this.el.select("table.x-date-inner tbody td");
24898         this.textNodes = this.el.query("table.x-date-inner tbody span");
24899
24900         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24901             text: "&#160;",
24902             tooltip: this.monthYearText
24903         });
24904
24905         this.mbtn.on('click', this.showMonthPicker, this);
24906         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24907
24908
24909         var today = (new Date()).dateFormat(this.format);
24910         
24911         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24912         if (this.showClear) {
24913             baseTb.add( new Roo.Toolbar.Fill());
24914         }
24915         baseTb.add({
24916             text: String.format(this.todayText, today),
24917             tooltip: String.format(this.todayTip, today),
24918             handler: this.selectToday,
24919             scope: this
24920         });
24921         
24922         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24923             
24924         //});
24925         if (this.showClear) {
24926             
24927             baseTb.add( new Roo.Toolbar.Fill());
24928             baseTb.add({
24929                 text: '&#160;',
24930                 cls: 'x-btn-icon x-btn-clear',
24931                 handler: function() {
24932                     //this.value = '';
24933                     this.fireEvent("select", this, '');
24934                 },
24935                 scope: this
24936             });
24937         }
24938         
24939         
24940         if(Roo.isIE){
24941             this.el.repaint();
24942         }
24943         this.update(this.value);
24944     },
24945
24946     createMonthPicker : function(){
24947         if(!this.monthPicker.dom.firstChild){
24948             var buf = ['<table border="0" cellspacing="0">'];
24949             for(var i = 0; i < 6; i++){
24950                 buf.push(
24951                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24952                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24953                     i == 0 ?
24954                     '<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>' :
24955                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24956                 );
24957             }
24958             buf.push(
24959                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24960                     this.okText,
24961                     '</button><button type="button" class="x-date-mp-cancel">',
24962                     this.cancelText,
24963                     '</button></td></tr>',
24964                 '</table>'
24965             );
24966             this.monthPicker.update(buf.join(''));
24967             this.monthPicker.on('click', this.onMonthClick, this);
24968             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24969
24970             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24971             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24972
24973             this.mpMonths.each(function(m, a, i){
24974                 i += 1;
24975                 if((i%2) == 0){
24976                     m.dom.xmonth = 5 + Math.round(i * .5);
24977                 }else{
24978                     m.dom.xmonth = Math.round((i-1) * .5);
24979                 }
24980             });
24981         }
24982     },
24983
24984     showMonthPicker : function(){
24985         this.createMonthPicker();
24986         var size = this.el.getSize();
24987         this.monthPicker.setSize(size);
24988         this.monthPicker.child('table').setSize(size);
24989
24990         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24991         this.updateMPMonth(this.mpSelMonth);
24992         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24993         this.updateMPYear(this.mpSelYear);
24994
24995         this.monthPicker.slideIn('t', {duration:.2});
24996     },
24997
24998     updateMPYear : function(y){
24999         this.mpyear = y;
25000         var ys = this.mpYears.elements;
25001         for(var i = 1; i <= 10; i++){
25002             var td = ys[i-1], y2;
25003             if((i%2) == 0){
25004                 y2 = y + Math.round(i * .5);
25005                 td.firstChild.innerHTML = y2;
25006                 td.xyear = y2;
25007             }else{
25008                 y2 = y - (5-Math.round(i * .5));
25009                 td.firstChild.innerHTML = y2;
25010                 td.xyear = y2;
25011             }
25012             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25013         }
25014     },
25015
25016     updateMPMonth : function(sm){
25017         this.mpMonths.each(function(m, a, i){
25018             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25019         });
25020     },
25021
25022     selectMPMonth: function(m){
25023         
25024     },
25025
25026     onMonthClick : function(e, t){
25027         e.stopEvent();
25028         var el = new Roo.Element(t), pn;
25029         if(el.is('button.x-date-mp-cancel')){
25030             this.hideMonthPicker();
25031         }
25032         else if(el.is('button.x-date-mp-ok')){
25033             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25034             this.hideMonthPicker();
25035         }
25036         else if(pn = el.up('td.x-date-mp-month', 2)){
25037             this.mpMonths.removeClass('x-date-mp-sel');
25038             pn.addClass('x-date-mp-sel');
25039             this.mpSelMonth = pn.dom.xmonth;
25040         }
25041         else if(pn = el.up('td.x-date-mp-year', 2)){
25042             this.mpYears.removeClass('x-date-mp-sel');
25043             pn.addClass('x-date-mp-sel');
25044             this.mpSelYear = pn.dom.xyear;
25045         }
25046         else if(el.is('a.x-date-mp-prev')){
25047             this.updateMPYear(this.mpyear-10);
25048         }
25049         else if(el.is('a.x-date-mp-next')){
25050             this.updateMPYear(this.mpyear+10);
25051         }
25052     },
25053
25054     onMonthDblClick : function(e, t){
25055         e.stopEvent();
25056         var el = new Roo.Element(t), pn;
25057         if(pn = el.up('td.x-date-mp-month', 2)){
25058             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25059             this.hideMonthPicker();
25060         }
25061         else if(pn = el.up('td.x-date-mp-year', 2)){
25062             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25063             this.hideMonthPicker();
25064         }
25065     },
25066
25067     hideMonthPicker : function(disableAnim){
25068         if(this.monthPicker){
25069             if(disableAnim === true){
25070                 this.monthPicker.hide();
25071             }else{
25072                 this.monthPicker.slideOut('t', {duration:.2});
25073             }
25074         }
25075     },
25076
25077     // private
25078     showPrevMonth : function(e){
25079         this.update(this.activeDate.add("mo", -1));
25080     },
25081
25082     // private
25083     showNextMonth : function(e){
25084         this.update(this.activeDate.add("mo", 1));
25085     },
25086
25087     // private
25088     showPrevYear : function(){
25089         this.update(this.activeDate.add("y", -1));
25090     },
25091
25092     // private
25093     showNextYear : function(){
25094         this.update(this.activeDate.add("y", 1));
25095     },
25096
25097     // private
25098     handleMouseWheel : function(e){
25099         var delta = e.getWheelDelta();
25100         if(delta > 0){
25101             this.showPrevMonth();
25102             e.stopEvent();
25103         } else if(delta < 0){
25104             this.showNextMonth();
25105             e.stopEvent();
25106         }
25107     },
25108
25109     // private
25110     handleDateClick : function(e, t){
25111         e.stopEvent();
25112         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25113             this.setValue(new Date(t.dateValue));
25114             this.fireEvent("select", this, this.value);
25115         }
25116     },
25117
25118     // private
25119     selectToday : function(){
25120         this.setValue(new Date().clearTime());
25121         this.fireEvent("select", this, this.value);
25122     },
25123
25124     // private
25125     update : function(date)
25126     {
25127         var vd = this.activeDate;
25128         this.activeDate = date;
25129         if(vd && this.el){
25130             var t = date.getTime();
25131             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25132                 this.cells.removeClass("x-date-selected");
25133                 this.cells.each(function(c){
25134                    if(c.dom.firstChild.dateValue == t){
25135                        c.addClass("x-date-selected");
25136                        setTimeout(function(){
25137                             try{c.dom.firstChild.focus();}catch(e){}
25138                        }, 50);
25139                        return false;
25140                    }
25141                 });
25142                 return;
25143             }
25144         }
25145         
25146         var days = date.getDaysInMonth();
25147         var firstOfMonth = date.getFirstDateOfMonth();
25148         var startingPos = firstOfMonth.getDay()-this.startDay;
25149
25150         if(startingPos <= this.startDay){
25151             startingPos += 7;
25152         }
25153
25154         var pm = date.add("mo", -1);
25155         var prevStart = pm.getDaysInMonth()-startingPos;
25156
25157         var cells = this.cells.elements;
25158         var textEls = this.textNodes;
25159         days += startingPos;
25160
25161         // convert everything to numbers so it's fast
25162         var day = 86400000;
25163         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25164         var today = new Date().clearTime().getTime();
25165         var sel = date.clearTime().getTime();
25166         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25167         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25168         var ddMatch = this.disabledDatesRE;
25169         var ddText = this.disabledDatesText;
25170         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25171         var ddaysText = this.disabledDaysText;
25172         var format = this.format;
25173
25174         var setCellClass = function(cal, cell){
25175             cell.title = "";
25176             var t = d.getTime();
25177             cell.firstChild.dateValue = t;
25178             if(t == today){
25179                 cell.className += " x-date-today";
25180                 cell.title = cal.todayText;
25181             }
25182             if(t == sel){
25183                 cell.className += " x-date-selected";
25184                 setTimeout(function(){
25185                     try{cell.firstChild.focus();}catch(e){}
25186                 }, 50);
25187             }
25188             // disabling
25189             if(t < min) {
25190                 cell.className = " x-date-disabled";
25191                 cell.title = cal.minText;
25192                 return;
25193             }
25194             if(t > max) {
25195                 cell.className = " x-date-disabled";
25196                 cell.title = cal.maxText;
25197                 return;
25198             }
25199             if(ddays){
25200                 if(ddays.indexOf(d.getDay()) != -1){
25201                     cell.title = ddaysText;
25202                     cell.className = " x-date-disabled";
25203                 }
25204             }
25205             if(ddMatch && format){
25206                 var fvalue = d.dateFormat(format);
25207                 if(ddMatch.test(fvalue)){
25208                     cell.title = ddText.replace("%0", fvalue);
25209                     cell.className = " x-date-disabled";
25210                 }
25211             }
25212         };
25213
25214         var i = 0;
25215         for(; i < startingPos; i++) {
25216             textEls[i].innerHTML = (++prevStart);
25217             d.setDate(d.getDate()+1);
25218             cells[i].className = "x-date-prevday";
25219             setCellClass(this, cells[i]);
25220         }
25221         for(; i < days; i++){
25222             intDay = i - startingPos + 1;
25223             textEls[i].innerHTML = (intDay);
25224             d.setDate(d.getDate()+1);
25225             cells[i].className = "x-date-active";
25226             setCellClass(this, cells[i]);
25227         }
25228         var extraDays = 0;
25229         for(; i < 42; i++) {
25230              textEls[i].innerHTML = (++extraDays);
25231              d.setDate(d.getDate()+1);
25232              cells[i].className = "x-date-nextday";
25233              setCellClass(this, cells[i]);
25234         }
25235
25236         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25237         this.fireEvent('monthchange', this, date);
25238         
25239         if(!this.internalRender){
25240             var main = this.el.dom.firstChild;
25241             var w = main.offsetWidth;
25242             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25243             Roo.fly(main).setWidth(w);
25244             this.internalRender = true;
25245             // opera does not respect the auto grow header center column
25246             // then, after it gets a width opera refuses to recalculate
25247             // without a second pass
25248             if(Roo.isOpera && !this.secondPass){
25249                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25250                 this.secondPass = true;
25251                 this.update.defer(10, this, [date]);
25252             }
25253         }
25254         
25255         
25256     }
25257 });        /*
25258  * Based on:
25259  * Ext JS Library 1.1.1
25260  * Copyright(c) 2006-2007, Ext JS, LLC.
25261  *
25262  * Originally Released Under LGPL - original licence link has changed is not relivant.
25263  *
25264  * Fork - LGPL
25265  * <script type="text/javascript">
25266  */
25267 /**
25268  * @class Roo.TabPanel
25269  * @extends Roo.util.Observable
25270  * A lightweight tab container.
25271  * <br><br>
25272  * Usage:
25273  * <pre><code>
25274 // basic tabs 1, built from existing content
25275 var tabs = new Roo.TabPanel("tabs1");
25276 tabs.addTab("script", "View Script");
25277 tabs.addTab("markup", "View Markup");
25278 tabs.activate("script");
25279
25280 // more advanced tabs, built from javascript
25281 var jtabs = new Roo.TabPanel("jtabs");
25282 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25283
25284 // set up the UpdateManager
25285 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25286 var updater = tab2.getUpdateManager();
25287 updater.setDefaultUrl("ajax1.htm");
25288 tab2.on('activate', updater.refresh, updater, true);
25289
25290 // Use setUrl for Ajax loading
25291 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25292 tab3.setUrl("ajax2.htm", null, true);
25293
25294 // Disabled tab
25295 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25296 tab4.disable();
25297
25298 jtabs.activate("jtabs-1");
25299  * </code></pre>
25300  * @constructor
25301  * Create a new TabPanel.
25302  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25303  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25304  */
25305 Roo.TabPanel = function(container, config){
25306     /**
25307     * The container element for this TabPanel.
25308     * @type Roo.Element
25309     */
25310     this.el = Roo.get(container, true);
25311     if(config){
25312         if(typeof config == "boolean"){
25313             this.tabPosition = config ? "bottom" : "top";
25314         }else{
25315             Roo.apply(this, config);
25316         }
25317     }
25318     if(this.tabPosition == "bottom"){
25319         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25320         this.el.addClass("x-tabs-bottom");
25321     }
25322     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25323     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25324     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25325     if(Roo.isIE){
25326         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25327     }
25328     if(this.tabPosition != "bottom"){
25329         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25330          * @type Roo.Element
25331          */
25332         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25333         this.el.addClass("x-tabs-top");
25334     }
25335     this.items = [];
25336
25337     this.bodyEl.setStyle("position", "relative");
25338
25339     this.active = null;
25340     this.activateDelegate = this.activate.createDelegate(this);
25341
25342     this.addEvents({
25343         /**
25344          * @event tabchange
25345          * Fires when the active tab changes
25346          * @param {Roo.TabPanel} this
25347          * @param {Roo.TabPanelItem} activePanel The new active tab
25348          */
25349         "tabchange": true,
25350         /**
25351          * @event beforetabchange
25352          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25353          * @param {Roo.TabPanel} this
25354          * @param {Object} e Set cancel to true on this object to cancel the tab change
25355          * @param {Roo.TabPanelItem} tab The tab being changed to
25356          */
25357         "beforetabchange" : true
25358     });
25359
25360     Roo.EventManager.onWindowResize(this.onResize, this);
25361     this.cpad = this.el.getPadding("lr");
25362     this.hiddenCount = 0;
25363
25364
25365     // toolbar on the tabbar support...
25366     if (this.toolbar) {
25367         var tcfg = this.toolbar;
25368         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25369         this.toolbar = new Roo.Toolbar(tcfg);
25370         if (Roo.isSafari) {
25371             var tbl = tcfg.container.child('table', true);
25372             tbl.setAttribute('width', '100%');
25373         }
25374         
25375     }
25376    
25377
25378
25379     Roo.TabPanel.superclass.constructor.call(this);
25380 };
25381
25382 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25383     /*
25384      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25385      */
25386     tabPosition : "top",
25387     /*
25388      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25389      */
25390     currentTabWidth : 0,
25391     /*
25392      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25393      */
25394     minTabWidth : 40,
25395     /*
25396      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25397      */
25398     maxTabWidth : 250,
25399     /*
25400      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25401      */
25402     preferredTabWidth : 175,
25403     /*
25404      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25405      */
25406     resizeTabs : false,
25407     /*
25408      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25409      */
25410     monitorResize : true,
25411     /*
25412      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25413      */
25414     toolbar : false,
25415
25416     /**
25417      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25418      * @param {String} id The id of the div to use <b>or create</b>
25419      * @param {String} text The text for the tab
25420      * @param {String} content (optional) Content to put in the TabPanelItem body
25421      * @param {Boolean} closable (optional) True to create a close icon on the tab
25422      * @return {Roo.TabPanelItem} The created TabPanelItem
25423      */
25424     addTab : function(id, text, content, closable){
25425         var item = new Roo.TabPanelItem(this, id, text, closable);
25426         this.addTabItem(item);
25427         if(content){
25428             item.setContent(content);
25429         }
25430         return item;
25431     },
25432
25433     /**
25434      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25435      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25436      * @return {Roo.TabPanelItem}
25437      */
25438     getTab : function(id){
25439         return this.items[id];
25440     },
25441
25442     /**
25443      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25444      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25445      */
25446     hideTab : function(id){
25447         var t = this.items[id];
25448         if(!t.isHidden()){
25449            t.setHidden(true);
25450            this.hiddenCount++;
25451            this.autoSizeTabs();
25452         }
25453     },
25454
25455     /**
25456      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25457      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25458      */
25459     unhideTab : function(id){
25460         var t = this.items[id];
25461         if(t.isHidden()){
25462            t.setHidden(false);
25463            this.hiddenCount--;
25464            this.autoSizeTabs();
25465         }
25466     },
25467
25468     /**
25469      * Adds an existing {@link Roo.TabPanelItem}.
25470      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25471      */
25472     addTabItem : function(item){
25473         this.items[item.id] = item;
25474         this.items.push(item);
25475         if(this.resizeTabs){
25476            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25477            this.autoSizeTabs();
25478         }else{
25479             item.autoSize();
25480         }
25481     },
25482
25483     /**
25484      * Removes a {@link Roo.TabPanelItem}.
25485      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25486      */
25487     removeTab : function(id){
25488         var items = this.items;
25489         var tab = items[id];
25490         if(!tab) { return; }
25491         var index = items.indexOf(tab);
25492         if(this.active == tab && items.length > 1){
25493             var newTab = this.getNextAvailable(index);
25494             if(newTab) {
25495                 newTab.activate();
25496             }
25497         }
25498         this.stripEl.dom.removeChild(tab.pnode.dom);
25499         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25500             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25501         }
25502         items.splice(index, 1);
25503         delete this.items[tab.id];
25504         tab.fireEvent("close", tab);
25505         tab.purgeListeners();
25506         this.autoSizeTabs();
25507     },
25508
25509     getNextAvailable : function(start){
25510         var items = this.items;
25511         var index = start;
25512         // look for a next tab that will slide over to
25513         // replace the one being removed
25514         while(index < items.length){
25515             var item = items[++index];
25516             if(item && !item.isHidden()){
25517                 return item;
25518             }
25519         }
25520         // if one isn't found select the previous tab (on the left)
25521         index = start;
25522         while(index >= 0){
25523             var item = items[--index];
25524             if(item && !item.isHidden()){
25525                 return item;
25526             }
25527         }
25528         return null;
25529     },
25530
25531     /**
25532      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25533      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25534      */
25535     disableTab : function(id){
25536         var tab = this.items[id];
25537         if(tab && this.active != tab){
25538             tab.disable();
25539         }
25540     },
25541
25542     /**
25543      * Enables a {@link Roo.TabPanelItem} that is disabled.
25544      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25545      */
25546     enableTab : function(id){
25547         var tab = this.items[id];
25548         tab.enable();
25549     },
25550
25551     /**
25552      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25553      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25554      * @return {Roo.TabPanelItem} The TabPanelItem.
25555      */
25556     activate : function(id){
25557         var tab = this.items[id];
25558         if(!tab){
25559             return null;
25560         }
25561         if(tab == this.active || tab.disabled){
25562             return tab;
25563         }
25564         var e = {};
25565         this.fireEvent("beforetabchange", this, e, tab);
25566         if(e.cancel !== true && !tab.disabled){
25567             if(this.active){
25568                 this.active.hide();
25569             }
25570             this.active = this.items[id];
25571             this.active.show();
25572             this.fireEvent("tabchange", this, this.active);
25573         }
25574         return tab;
25575     },
25576
25577     /**
25578      * Gets the active {@link Roo.TabPanelItem}.
25579      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25580      */
25581     getActiveTab : function(){
25582         return this.active;
25583     },
25584
25585     /**
25586      * Updates the tab body element to fit the height of the container element
25587      * for overflow scrolling
25588      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25589      */
25590     syncHeight : function(targetHeight){
25591         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25592         var bm = this.bodyEl.getMargins();
25593         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25594         this.bodyEl.setHeight(newHeight);
25595         return newHeight;
25596     },
25597
25598     onResize : function(){
25599         if(this.monitorResize){
25600             this.autoSizeTabs();
25601         }
25602     },
25603
25604     /**
25605      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25606      */
25607     beginUpdate : function(){
25608         this.updating = true;
25609     },
25610
25611     /**
25612      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25613      */
25614     endUpdate : function(){
25615         this.updating = false;
25616         this.autoSizeTabs();
25617     },
25618
25619     /**
25620      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25621      */
25622     autoSizeTabs : function(){
25623         var count = this.items.length;
25624         var vcount = count - this.hiddenCount;
25625         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25626         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25627         var availWidth = Math.floor(w / vcount);
25628         var b = this.stripBody;
25629         if(b.getWidth() > w){
25630             var tabs = this.items;
25631             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25632             if(availWidth < this.minTabWidth){
25633                 /*if(!this.sleft){    // incomplete scrolling code
25634                     this.createScrollButtons();
25635                 }
25636                 this.showScroll();
25637                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25638             }
25639         }else{
25640             if(this.currentTabWidth < this.preferredTabWidth){
25641                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25642             }
25643         }
25644     },
25645
25646     /**
25647      * Returns the number of tabs in this TabPanel.
25648      * @return {Number}
25649      */
25650      getCount : function(){
25651          return this.items.length;
25652      },
25653
25654     /**
25655      * Resizes all the tabs to the passed width
25656      * @param {Number} The new width
25657      */
25658     setTabWidth : function(width){
25659         this.currentTabWidth = width;
25660         for(var i = 0, len = this.items.length; i < len; i++) {
25661                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25662         }
25663     },
25664
25665     /**
25666      * Destroys this TabPanel
25667      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25668      */
25669     destroy : function(removeEl){
25670         Roo.EventManager.removeResizeListener(this.onResize, this);
25671         for(var i = 0, len = this.items.length; i < len; i++){
25672             this.items[i].purgeListeners();
25673         }
25674         if(removeEl === true){
25675             this.el.update("");
25676             this.el.remove();
25677         }
25678     }
25679 });
25680
25681 /**
25682  * @class Roo.TabPanelItem
25683  * @extends Roo.util.Observable
25684  * Represents an individual item (tab plus body) in a TabPanel.
25685  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25686  * @param {String} id The id of this TabPanelItem
25687  * @param {String} text The text for the tab of this TabPanelItem
25688  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25689  */
25690 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25691     /**
25692      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25693      * @type Roo.TabPanel
25694      */
25695     this.tabPanel = tabPanel;
25696     /**
25697      * The id for this TabPanelItem
25698      * @type String
25699      */
25700     this.id = id;
25701     /** @private */
25702     this.disabled = false;
25703     /** @private */
25704     this.text = text;
25705     /** @private */
25706     this.loaded = false;
25707     this.closable = closable;
25708
25709     /**
25710      * The body element for this TabPanelItem.
25711      * @type Roo.Element
25712      */
25713     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25714     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25715     this.bodyEl.setStyle("display", "block");
25716     this.bodyEl.setStyle("zoom", "1");
25717     this.hideAction();
25718
25719     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25720     /** @private */
25721     this.el = Roo.get(els.el, true);
25722     this.inner = Roo.get(els.inner, true);
25723     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25724     this.pnode = Roo.get(els.el.parentNode, true);
25725     this.el.on("mousedown", this.onTabMouseDown, this);
25726     this.el.on("click", this.onTabClick, this);
25727     /** @private */
25728     if(closable){
25729         var c = Roo.get(els.close, true);
25730         c.dom.title = this.closeText;
25731         c.addClassOnOver("close-over");
25732         c.on("click", this.closeClick, this);
25733      }
25734
25735     this.addEvents({
25736          /**
25737          * @event activate
25738          * Fires when this tab becomes the active tab.
25739          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25740          * @param {Roo.TabPanelItem} this
25741          */
25742         "activate": true,
25743         /**
25744          * @event beforeclose
25745          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25746          * @param {Roo.TabPanelItem} this
25747          * @param {Object} e Set cancel to true on this object to cancel the close.
25748          */
25749         "beforeclose": true,
25750         /**
25751          * @event close
25752          * Fires when this tab is closed.
25753          * @param {Roo.TabPanelItem} this
25754          */
25755          "close": true,
25756         /**
25757          * @event deactivate
25758          * Fires when this tab is no longer the active tab.
25759          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25760          * @param {Roo.TabPanelItem} this
25761          */
25762          "deactivate" : true
25763     });
25764     this.hidden = false;
25765
25766     Roo.TabPanelItem.superclass.constructor.call(this);
25767 };
25768
25769 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25770     purgeListeners : function(){
25771        Roo.util.Observable.prototype.purgeListeners.call(this);
25772        this.el.removeAllListeners();
25773     },
25774     /**
25775      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25776      */
25777     show : function(){
25778         this.pnode.addClass("on");
25779         this.showAction();
25780         if(Roo.isOpera){
25781             this.tabPanel.stripWrap.repaint();
25782         }
25783         this.fireEvent("activate", this.tabPanel, this);
25784     },
25785
25786     /**
25787      * Returns true if this tab is the active tab.
25788      * @return {Boolean}
25789      */
25790     isActive : function(){
25791         return this.tabPanel.getActiveTab() == this;
25792     },
25793
25794     /**
25795      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25796      */
25797     hide : function(){
25798         this.pnode.removeClass("on");
25799         this.hideAction();
25800         this.fireEvent("deactivate", this.tabPanel, this);
25801     },
25802
25803     hideAction : function(){
25804         this.bodyEl.hide();
25805         this.bodyEl.setStyle("position", "absolute");
25806         this.bodyEl.setLeft("-20000px");
25807         this.bodyEl.setTop("-20000px");
25808     },
25809
25810     showAction : function(){
25811         this.bodyEl.setStyle("position", "relative");
25812         this.bodyEl.setTop("");
25813         this.bodyEl.setLeft("");
25814         this.bodyEl.show();
25815     },
25816
25817     /**
25818      * Set the tooltip for the tab.
25819      * @param {String} tooltip The tab's tooltip
25820      */
25821     setTooltip : function(text){
25822         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25823             this.textEl.dom.qtip = text;
25824             this.textEl.dom.removeAttribute('title');
25825         }else{
25826             this.textEl.dom.title = text;
25827         }
25828     },
25829
25830     onTabClick : function(e){
25831         e.preventDefault();
25832         this.tabPanel.activate(this.id);
25833     },
25834
25835     onTabMouseDown : function(e){
25836         e.preventDefault();
25837         this.tabPanel.activate(this.id);
25838     },
25839
25840     getWidth : function(){
25841         return this.inner.getWidth();
25842     },
25843
25844     setWidth : function(width){
25845         var iwidth = width - this.pnode.getPadding("lr");
25846         this.inner.setWidth(iwidth);
25847         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25848         this.pnode.setWidth(width);
25849     },
25850
25851     /**
25852      * Show or hide the tab
25853      * @param {Boolean} hidden True to hide or false to show.
25854      */
25855     setHidden : function(hidden){
25856         this.hidden = hidden;
25857         this.pnode.setStyle("display", hidden ? "none" : "");
25858     },
25859
25860     /**
25861      * Returns true if this tab is "hidden"
25862      * @return {Boolean}
25863      */
25864     isHidden : function(){
25865         return this.hidden;
25866     },
25867
25868     /**
25869      * Returns the text for this tab
25870      * @return {String}
25871      */
25872     getText : function(){
25873         return this.text;
25874     },
25875
25876     autoSize : function(){
25877         //this.el.beginMeasure();
25878         this.textEl.setWidth(1);
25879         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25880         //this.el.endMeasure();
25881     },
25882
25883     /**
25884      * Sets the text for the tab (Note: this also sets the tooltip text)
25885      * @param {String} text The tab's text and tooltip
25886      */
25887     setText : function(text){
25888         this.text = text;
25889         this.textEl.update(text);
25890         this.setTooltip(text);
25891         if(!this.tabPanel.resizeTabs){
25892             this.autoSize();
25893         }
25894     },
25895     /**
25896      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25897      */
25898     activate : function(){
25899         this.tabPanel.activate(this.id);
25900     },
25901
25902     /**
25903      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25904      */
25905     disable : function(){
25906         if(this.tabPanel.active != this){
25907             this.disabled = true;
25908             this.pnode.addClass("disabled");
25909         }
25910     },
25911
25912     /**
25913      * Enables this TabPanelItem if it was previously disabled.
25914      */
25915     enable : function(){
25916         this.disabled = false;
25917         this.pnode.removeClass("disabled");
25918     },
25919
25920     /**
25921      * Sets the content for this TabPanelItem.
25922      * @param {String} content The content
25923      * @param {Boolean} loadScripts true to look for and load scripts
25924      */
25925     setContent : function(content, loadScripts){
25926         this.bodyEl.update(content, loadScripts);
25927     },
25928
25929     /**
25930      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25931      * @return {Roo.UpdateManager} The UpdateManager
25932      */
25933     getUpdateManager : function(){
25934         return this.bodyEl.getUpdateManager();
25935     },
25936
25937     /**
25938      * Set a URL to be used to load the content for this TabPanelItem.
25939      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25940      * @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)
25941      * @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)
25942      * @return {Roo.UpdateManager} The UpdateManager
25943      */
25944     setUrl : function(url, params, loadOnce){
25945         if(this.refreshDelegate){
25946             this.un('activate', this.refreshDelegate);
25947         }
25948         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25949         this.on("activate", this.refreshDelegate);
25950         return this.bodyEl.getUpdateManager();
25951     },
25952
25953     /** @private */
25954     _handleRefresh : function(url, params, loadOnce){
25955         if(!loadOnce || !this.loaded){
25956             var updater = this.bodyEl.getUpdateManager();
25957             updater.update(url, params, this._setLoaded.createDelegate(this));
25958         }
25959     },
25960
25961     /**
25962      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25963      *   Will fail silently if the setUrl method has not been called.
25964      *   This does not activate the panel, just updates its content.
25965      */
25966     refresh : function(){
25967         if(this.refreshDelegate){
25968            this.loaded = false;
25969            this.refreshDelegate();
25970         }
25971     },
25972
25973     /** @private */
25974     _setLoaded : function(){
25975         this.loaded = true;
25976     },
25977
25978     /** @private */
25979     closeClick : function(e){
25980         var o = {};
25981         e.stopEvent();
25982         this.fireEvent("beforeclose", this, o);
25983         if(o.cancel !== true){
25984             this.tabPanel.removeTab(this.id);
25985         }
25986     },
25987     /**
25988      * The text displayed in the tooltip for the close icon.
25989      * @type String
25990      */
25991     closeText : "Close this tab"
25992 });
25993
25994 /** @private */
25995 Roo.TabPanel.prototype.createStrip = function(container){
25996     var strip = document.createElement("div");
25997     strip.className = "x-tabs-wrap";
25998     container.appendChild(strip);
25999     return strip;
26000 };
26001 /** @private */
26002 Roo.TabPanel.prototype.createStripList = function(strip){
26003     // div wrapper for retard IE
26004     // returns the "tr" element.
26005     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26006         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26007         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26008     return strip.firstChild.firstChild.firstChild.firstChild;
26009 };
26010 /** @private */
26011 Roo.TabPanel.prototype.createBody = function(container){
26012     var body = document.createElement("div");
26013     Roo.id(body, "tab-body");
26014     Roo.fly(body).addClass("x-tabs-body");
26015     container.appendChild(body);
26016     return body;
26017 };
26018 /** @private */
26019 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26020     var body = Roo.getDom(id);
26021     if(!body){
26022         body = document.createElement("div");
26023         body.id = id;
26024     }
26025     Roo.fly(body).addClass("x-tabs-item-body");
26026     bodyEl.insertBefore(body, bodyEl.firstChild);
26027     return body;
26028 };
26029 /** @private */
26030 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26031     var td = document.createElement("td");
26032     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26033     //stripEl.appendChild(td);
26034     if(closable){
26035         td.className = "x-tabs-closable";
26036         if(!this.closeTpl){
26037             this.closeTpl = new Roo.Template(
26038                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26039                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26040                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26041             );
26042         }
26043         var el = this.closeTpl.overwrite(td, {"text": text});
26044         var close = el.getElementsByTagName("div")[0];
26045         var inner = el.getElementsByTagName("em")[0];
26046         return {"el": el, "close": close, "inner": inner};
26047     } else {
26048         if(!this.tabTpl){
26049             this.tabTpl = new Roo.Template(
26050                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26051                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26052             );
26053         }
26054         var el = this.tabTpl.overwrite(td, {"text": text});
26055         var inner = el.getElementsByTagName("em")[0];
26056         return {"el": el, "inner": inner};
26057     }
26058 };/*
26059  * Based on:
26060  * Ext JS Library 1.1.1
26061  * Copyright(c) 2006-2007, Ext JS, LLC.
26062  *
26063  * Originally Released Under LGPL - original licence link has changed is not relivant.
26064  *
26065  * Fork - LGPL
26066  * <script type="text/javascript">
26067  */
26068
26069 /**
26070  * @class Roo.Button
26071  * @extends Roo.util.Observable
26072  * Simple Button class
26073  * @cfg {String} text The button text
26074  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26075  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26076  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26077  * @cfg {Object} scope The scope of the handler
26078  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26079  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26080  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26081  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26082  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26083  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26084    applies if enableToggle = true)
26085  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26086  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26087   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26088  * @constructor
26089  * Create a new button
26090  * @param {Object} config The config object
26091  */
26092 Roo.Button = function(renderTo, config)
26093 {
26094     if (!config) {
26095         config = renderTo;
26096         renderTo = config.renderTo || false;
26097     }
26098     
26099     Roo.apply(this, config);
26100     this.addEvents({
26101         /**
26102              * @event click
26103              * Fires when this button is clicked
26104              * @param {Button} this
26105              * @param {EventObject} e The click event
26106              */
26107             "click" : true,
26108         /**
26109              * @event toggle
26110              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26111              * @param {Button} this
26112              * @param {Boolean} pressed
26113              */
26114             "toggle" : true,
26115         /**
26116              * @event mouseover
26117              * Fires when the mouse hovers over the button
26118              * @param {Button} this
26119              * @param {Event} e The event object
26120              */
26121         'mouseover' : true,
26122         /**
26123              * @event mouseout
26124              * Fires when the mouse exits the button
26125              * @param {Button} this
26126              * @param {Event} e The event object
26127              */
26128         'mouseout': true,
26129          /**
26130              * @event render
26131              * Fires when the button is rendered
26132              * @param {Button} this
26133              */
26134         'render': true
26135     });
26136     if(this.menu){
26137         this.menu = Roo.menu.MenuMgr.get(this.menu);
26138     }
26139     // register listeners first!!  - so render can be captured..
26140     Roo.util.Observable.call(this);
26141     if(renderTo){
26142         this.render(renderTo);
26143     }
26144     
26145   
26146 };
26147
26148 Roo.extend(Roo.Button, Roo.util.Observable, {
26149     /**
26150      * 
26151      */
26152     
26153     /**
26154      * Read-only. True if this button is hidden
26155      * @type Boolean
26156      */
26157     hidden : false,
26158     /**
26159      * Read-only. True if this button is disabled
26160      * @type Boolean
26161      */
26162     disabled : false,
26163     /**
26164      * Read-only. True if this button is pressed (only if enableToggle = true)
26165      * @type Boolean
26166      */
26167     pressed : false,
26168
26169     /**
26170      * @cfg {Number} tabIndex 
26171      * The DOM tabIndex for this button (defaults to undefined)
26172      */
26173     tabIndex : undefined,
26174
26175     /**
26176      * @cfg {Boolean} enableToggle
26177      * True to enable pressed/not pressed toggling (defaults to false)
26178      */
26179     enableToggle: false,
26180     /**
26181      * @cfg {Mixed} menu
26182      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26183      */
26184     menu : undefined,
26185     /**
26186      * @cfg {String} menuAlign
26187      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26188      */
26189     menuAlign : "tl-bl?",
26190
26191     /**
26192      * @cfg {String} iconCls
26193      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26194      */
26195     iconCls : undefined,
26196     /**
26197      * @cfg {String} type
26198      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26199      */
26200     type : 'button',
26201
26202     // private
26203     menuClassTarget: 'tr',
26204
26205     /**
26206      * @cfg {String} clickEvent
26207      * The type of event to map to the button's event handler (defaults to 'click')
26208      */
26209     clickEvent : 'click',
26210
26211     /**
26212      * @cfg {Boolean} handleMouseEvents
26213      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26214      */
26215     handleMouseEvents : true,
26216
26217     /**
26218      * @cfg {String} tooltipType
26219      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26220      */
26221     tooltipType : 'qtip',
26222
26223     /**
26224      * @cfg {String} cls
26225      * A CSS class to apply to the button's main element.
26226      */
26227     
26228     /**
26229      * @cfg {Roo.Template} template (Optional)
26230      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26231      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26232      * require code modifications if required elements (e.g. a button) aren't present.
26233      */
26234
26235     // private
26236     render : function(renderTo){
26237         var btn;
26238         if(this.hideParent){
26239             this.parentEl = Roo.get(renderTo);
26240         }
26241         if(!this.dhconfig){
26242             if(!this.template){
26243                 if(!Roo.Button.buttonTemplate){
26244                     // hideous table template
26245                     Roo.Button.buttonTemplate = new Roo.Template(
26246                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26247                         '<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>',
26248                         "</tr></tbody></table>");
26249                 }
26250                 this.template = Roo.Button.buttonTemplate;
26251             }
26252             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26253             var btnEl = btn.child("button:first");
26254             btnEl.on('focus', this.onFocus, this);
26255             btnEl.on('blur', this.onBlur, this);
26256             if(this.cls){
26257                 btn.addClass(this.cls);
26258             }
26259             if(this.icon){
26260                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26261             }
26262             if(this.iconCls){
26263                 btnEl.addClass(this.iconCls);
26264                 if(!this.cls){
26265                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26266                 }
26267             }
26268             if(this.tabIndex !== undefined){
26269                 btnEl.dom.tabIndex = this.tabIndex;
26270             }
26271             if(this.tooltip){
26272                 if(typeof this.tooltip == 'object'){
26273                     Roo.QuickTips.tips(Roo.apply({
26274                           target: btnEl.id
26275                     }, this.tooltip));
26276                 } else {
26277                     btnEl.dom[this.tooltipType] = this.tooltip;
26278                 }
26279             }
26280         }else{
26281             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26282         }
26283         this.el = btn;
26284         if(this.id){
26285             this.el.dom.id = this.el.id = this.id;
26286         }
26287         if(this.menu){
26288             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26289             this.menu.on("show", this.onMenuShow, this);
26290             this.menu.on("hide", this.onMenuHide, this);
26291         }
26292         btn.addClass("x-btn");
26293         if(Roo.isIE && !Roo.isIE7){
26294             this.autoWidth.defer(1, this);
26295         }else{
26296             this.autoWidth();
26297         }
26298         if(this.handleMouseEvents){
26299             btn.on("mouseover", this.onMouseOver, this);
26300             btn.on("mouseout", this.onMouseOut, this);
26301             btn.on("mousedown", this.onMouseDown, this);
26302         }
26303         btn.on(this.clickEvent, this.onClick, this);
26304         //btn.on("mouseup", this.onMouseUp, this);
26305         if(this.hidden){
26306             this.hide();
26307         }
26308         if(this.disabled){
26309             this.disable();
26310         }
26311         Roo.ButtonToggleMgr.register(this);
26312         if(this.pressed){
26313             this.el.addClass("x-btn-pressed");
26314         }
26315         if(this.repeat){
26316             var repeater = new Roo.util.ClickRepeater(btn,
26317                 typeof this.repeat == "object" ? this.repeat : {}
26318             );
26319             repeater.on("click", this.onClick,  this);
26320         }
26321         
26322         this.fireEvent('render', this);
26323         
26324     },
26325     /**
26326      * Returns the button's underlying element
26327      * @return {Roo.Element} The element
26328      */
26329     getEl : function(){
26330         return this.el;  
26331     },
26332     
26333     /**
26334      * Destroys this Button and removes any listeners.
26335      */
26336     destroy : function(){
26337         Roo.ButtonToggleMgr.unregister(this);
26338         this.el.removeAllListeners();
26339         this.purgeListeners();
26340         this.el.remove();
26341     },
26342
26343     // private
26344     autoWidth : function(){
26345         if(this.el){
26346             this.el.setWidth("auto");
26347             if(Roo.isIE7 && Roo.isStrict){
26348                 var ib = this.el.child('button');
26349                 if(ib && ib.getWidth() > 20){
26350                     ib.clip();
26351                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26352                 }
26353             }
26354             if(this.minWidth){
26355                 if(this.hidden){
26356                     this.el.beginMeasure();
26357                 }
26358                 if(this.el.getWidth() < this.minWidth){
26359                     this.el.setWidth(this.minWidth);
26360                 }
26361                 if(this.hidden){
26362                     this.el.endMeasure();
26363                 }
26364             }
26365         }
26366     },
26367
26368     /**
26369      * Assigns this button's click handler
26370      * @param {Function} handler The function to call when the button is clicked
26371      * @param {Object} scope (optional) Scope for the function passed in
26372      */
26373     setHandler : function(handler, scope){
26374         this.handler = handler;
26375         this.scope = scope;  
26376     },
26377     
26378     /**
26379      * Sets this button's text
26380      * @param {String} text The button text
26381      */
26382     setText : function(text){
26383         this.text = text;
26384         if(this.el){
26385             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26386         }
26387         this.autoWidth();
26388     },
26389     
26390     /**
26391      * Gets the text for this button
26392      * @return {String} The button text
26393      */
26394     getText : function(){
26395         return this.text;  
26396     },
26397     
26398     /**
26399      * Show this button
26400      */
26401     show: function(){
26402         this.hidden = false;
26403         if(this.el){
26404             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26405         }
26406     },
26407     
26408     /**
26409      * Hide this button
26410      */
26411     hide: function(){
26412         this.hidden = true;
26413         if(this.el){
26414             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26415         }
26416     },
26417     
26418     /**
26419      * Convenience function for boolean show/hide
26420      * @param {Boolean} visible True to show, false to hide
26421      */
26422     setVisible: function(visible){
26423         if(visible) {
26424             this.show();
26425         }else{
26426             this.hide();
26427         }
26428     },
26429     
26430     /**
26431      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26432      * @param {Boolean} state (optional) Force a particular state
26433      */
26434     toggle : function(state){
26435         state = state === undefined ? !this.pressed : state;
26436         if(state != this.pressed){
26437             if(state){
26438                 this.el.addClass("x-btn-pressed");
26439                 this.pressed = true;
26440                 this.fireEvent("toggle", this, true);
26441             }else{
26442                 this.el.removeClass("x-btn-pressed");
26443                 this.pressed = false;
26444                 this.fireEvent("toggle", this, false);
26445             }
26446             if(this.toggleHandler){
26447                 this.toggleHandler.call(this.scope || this, this, state);
26448             }
26449         }
26450     },
26451     
26452     /**
26453      * Focus the button
26454      */
26455     focus : function(){
26456         this.el.child('button:first').focus();
26457     },
26458     
26459     /**
26460      * Disable this button
26461      */
26462     disable : function(){
26463         if(this.el){
26464             this.el.addClass("x-btn-disabled");
26465         }
26466         this.disabled = true;
26467     },
26468     
26469     /**
26470      * Enable this button
26471      */
26472     enable : function(){
26473         if(this.el){
26474             this.el.removeClass("x-btn-disabled");
26475         }
26476         this.disabled = false;
26477     },
26478
26479     /**
26480      * Convenience function for boolean enable/disable
26481      * @param {Boolean} enabled True to enable, false to disable
26482      */
26483     setDisabled : function(v){
26484         this[v !== true ? "enable" : "disable"]();
26485     },
26486
26487     // private
26488     onClick : function(e){
26489         if(e){
26490             e.preventDefault();
26491         }
26492         if(e.button != 0){
26493             return;
26494         }
26495         if(!this.disabled){
26496             if(this.enableToggle){
26497                 this.toggle();
26498             }
26499             if(this.menu && !this.menu.isVisible()){
26500                 this.menu.show(this.el, this.menuAlign);
26501             }
26502             this.fireEvent("click", this, e);
26503             if(this.handler){
26504                 this.el.removeClass("x-btn-over");
26505                 this.handler.call(this.scope || this, this, e);
26506             }
26507         }
26508     },
26509     // private
26510     onMouseOver : function(e){
26511         if(!this.disabled){
26512             this.el.addClass("x-btn-over");
26513             this.fireEvent('mouseover', this, e);
26514         }
26515     },
26516     // private
26517     onMouseOut : function(e){
26518         if(!e.within(this.el,  true)){
26519             this.el.removeClass("x-btn-over");
26520             this.fireEvent('mouseout', this, e);
26521         }
26522     },
26523     // private
26524     onFocus : function(e){
26525         if(!this.disabled){
26526             this.el.addClass("x-btn-focus");
26527         }
26528     },
26529     // private
26530     onBlur : function(e){
26531         this.el.removeClass("x-btn-focus");
26532     },
26533     // private
26534     onMouseDown : function(e){
26535         if(!this.disabled && e.button == 0){
26536             this.el.addClass("x-btn-click");
26537             Roo.get(document).on('mouseup', this.onMouseUp, this);
26538         }
26539     },
26540     // private
26541     onMouseUp : function(e){
26542         if(e.button == 0){
26543             this.el.removeClass("x-btn-click");
26544             Roo.get(document).un('mouseup', this.onMouseUp, this);
26545         }
26546     },
26547     // private
26548     onMenuShow : function(e){
26549         this.el.addClass("x-btn-menu-active");
26550     },
26551     // private
26552     onMenuHide : function(e){
26553         this.el.removeClass("x-btn-menu-active");
26554     }   
26555 });
26556
26557 // Private utility class used by Button
26558 Roo.ButtonToggleMgr = function(){
26559    var groups = {};
26560    
26561    function toggleGroup(btn, state){
26562        if(state){
26563            var g = groups[btn.toggleGroup];
26564            for(var i = 0, l = g.length; i < l; i++){
26565                if(g[i] != btn){
26566                    g[i].toggle(false);
26567                }
26568            }
26569        }
26570    }
26571    
26572    return {
26573        register : function(btn){
26574            if(!btn.toggleGroup){
26575                return;
26576            }
26577            var g = groups[btn.toggleGroup];
26578            if(!g){
26579                g = groups[btn.toggleGroup] = [];
26580            }
26581            g.push(btn);
26582            btn.on("toggle", toggleGroup);
26583        },
26584        
26585        unregister : function(btn){
26586            if(!btn.toggleGroup){
26587                return;
26588            }
26589            var g = groups[btn.toggleGroup];
26590            if(g){
26591                g.remove(btn);
26592                btn.un("toggle", toggleGroup);
26593            }
26594        }
26595    };
26596 }();/*
26597  * Based on:
26598  * Ext JS Library 1.1.1
26599  * Copyright(c) 2006-2007, Ext JS, LLC.
26600  *
26601  * Originally Released Under LGPL - original licence link has changed is not relivant.
26602  *
26603  * Fork - LGPL
26604  * <script type="text/javascript">
26605  */
26606  
26607 /**
26608  * @class Roo.SplitButton
26609  * @extends Roo.Button
26610  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26611  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26612  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26613  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26614  * @cfg {String} arrowTooltip The title attribute of the arrow
26615  * @constructor
26616  * Create a new menu button
26617  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26618  * @param {Object} config The config object
26619  */
26620 Roo.SplitButton = function(renderTo, config){
26621     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26622     /**
26623      * @event arrowclick
26624      * Fires when this button's arrow is clicked
26625      * @param {SplitButton} this
26626      * @param {EventObject} e The click event
26627      */
26628     this.addEvents({"arrowclick":true});
26629 };
26630
26631 Roo.extend(Roo.SplitButton, Roo.Button, {
26632     render : function(renderTo){
26633         // this is one sweet looking template!
26634         var tpl = new Roo.Template(
26635             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26636             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26637             '<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>',
26638             "</tbody></table></td><td>",
26639             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26640             '<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>',
26641             "</tbody></table></td></tr></table>"
26642         );
26643         var btn = tpl.append(renderTo, [this.text, this.type], true);
26644         var btnEl = btn.child("button");
26645         if(this.cls){
26646             btn.addClass(this.cls);
26647         }
26648         if(this.icon){
26649             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26650         }
26651         if(this.iconCls){
26652             btnEl.addClass(this.iconCls);
26653             if(!this.cls){
26654                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26655             }
26656         }
26657         this.el = btn;
26658         if(this.handleMouseEvents){
26659             btn.on("mouseover", this.onMouseOver, this);
26660             btn.on("mouseout", this.onMouseOut, this);
26661             btn.on("mousedown", this.onMouseDown, this);
26662             btn.on("mouseup", this.onMouseUp, this);
26663         }
26664         btn.on(this.clickEvent, this.onClick, this);
26665         if(this.tooltip){
26666             if(typeof this.tooltip == 'object'){
26667                 Roo.QuickTips.tips(Roo.apply({
26668                       target: btnEl.id
26669                 }, this.tooltip));
26670             } else {
26671                 btnEl.dom[this.tooltipType] = this.tooltip;
26672             }
26673         }
26674         if(this.arrowTooltip){
26675             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26676         }
26677         if(this.hidden){
26678             this.hide();
26679         }
26680         if(this.disabled){
26681             this.disable();
26682         }
26683         if(this.pressed){
26684             this.el.addClass("x-btn-pressed");
26685         }
26686         if(Roo.isIE && !Roo.isIE7){
26687             this.autoWidth.defer(1, this);
26688         }else{
26689             this.autoWidth();
26690         }
26691         if(this.menu){
26692             this.menu.on("show", this.onMenuShow, this);
26693             this.menu.on("hide", this.onMenuHide, this);
26694         }
26695         this.fireEvent('render', this);
26696     },
26697
26698     // private
26699     autoWidth : function(){
26700         if(this.el){
26701             var tbl = this.el.child("table:first");
26702             var tbl2 = this.el.child("table:last");
26703             this.el.setWidth("auto");
26704             tbl.setWidth("auto");
26705             if(Roo.isIE7 && Roo.isStrict){
26706                 var ib = this.el.child('button:first');
26707                 if(ib && ib.getWidth() > 20){
26708                     ib.clip();
26709                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26710                 }
26711             }
26712             if(this.minWidth){
26713                 if(this.hidden){
26714                     this.el.beginMeasure();
26715                 }
26716                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26717                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26718                 }
26719                 if(this.hidden){
26720                     this.el.endMeasure();
26721                 }
26722             }
26723             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26724         } 
26725     },
26726     /**
26727      * Sets this button's click handler
26728      * @param {Function} handler The function to call when the button is clicked
26729      * @param {Object} scope (optional) Scope for the function passed above
26730      */
26731     setHandler : function(handler, scope){
26732         this.handler = handler;
26733         this.scope = scope;  
26734     },
26735     
26736     /**
26737      * Sets this button's arrow click handler
26738      * @param {Function} handler The function to call when the arrow is clicked
26739      * @param {Object} scope (optional) Scope for the function passed above
26740      */
26741     setArrowHandler : function(handler, scope){
26742         this.arrowHandler = handler;
26743         this.scope = scope;  
26744     },
26745     
26746     /**
26747      * Focus the button
26748      */
26749     focus : function(){
26750         if(this.el){
26751             this.el.child("button:first").focus();
26752         }
26753     },
26754
26755     // private
26756     onClick : function(e){
26757         e.preventDefault();
26758         if(!this.disabled){
26759             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26760                 if(this.menu && !this.menu.isVisible()){
26761                     this.menu.show(this.el, this.menuAlign);
26762                 }
26763                 this.fireEvent("arrowclick", this, e);
26764                 if(this.arrowHandler){
26765                     this.arrowHandler.call(this.scope || this, this, e);
26766                 }
26767             }else{
26768                 this.fireEvent("click", this, e);
26769                 if(this.handler){
26770                     this.handler.call(this.scope || this, this, e);
26771                 }
26772             }
26773         }
26774     },
26775     // private
26776     onMouseDown : function(e){
26777         if(!this.disabled){
26778             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26779         }
26780     },
26781     // private
26782     onMouseUp : function(e){
26783         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26784     }   
26785 });
26786
26787
26788 // backwards compat
26789 Roo.MenuButton = Roo.SplitButton;/*
26790  * Based on:
26791  * Ext JS Library 1.1.1
26792  * Copyright(c) 2006-2007, Ext JS, LLC.
26793  *
26794  * Originally Released Under LGPL - original licence link has changed is not relivant.
26795  *
26796  * Fork - LGPL
26797  * <script type="text/javascript">
26798  */
26799
26800 /**
26801  * @class Roo.Toolbar
26802  * Basic Toolbar class.
26803  * @constructor
26804  * Creates a new Toolbar
26805  * @param {Object} container The config object
26806  */ 
26807 Roo.Toolbar = function(container, buttons, config)
26808 {
26809     /// old consturctor format still supported..
26810     if(container instanceof Array){ // omit the container for later rendering
26811         buttons = container;
26812         config = buttons;
26813         container = null;
26814     }
26815     if (typeof(container) == 'object' && container.xtype) {
26816         config = container;
26817         container = config.container;
26818         buttons = config.buttons || []; // not really - use items!!
26819     }
26820     var xitems = [];
26821     if (config && config.items) {
26822         xitems = config.items;
26823         delete config.items;
26824     }
26825     Roo.apply(this, config);
26826     this.buttons = buttons;
26827     
26828     if(container){
26829         this.render(container);
26830     }
26831     this.xitems = xitems;
26832     Roo.each(xitems, function(b) {
26833         this.add(b);
26834     }, this);
26835     
26836 };
26837
26838 Roo.Toolbar.prototype = {
26839     /**
26840      * @cfg {Array} items
26841      * array of button configs or elements to add (will be converted to a MixedCollection)
26842      */
26843     
26844     /**
26845      * @cfg {String/HTMLElement/Element} container
26846      * The id or element that will contain the toolbar
26847      */
26848     // private
26849     render : function(ct){
26850         this.el = Roo.get(ct);
26851         if(this.cls){
26852             this.el.addClass(this.cls);
26853         }
26854         // using a table allows for vertical alignment
26855         // 100% width is needed by Safari...
26856         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26857         this.tr = this.el.child("tr", true);
26858         var autoId = 0;
26859         this.items = new Roo.util.MixedCollection(false, function(o){
26860             return o.id || ("item" + (++autoId));
26861         });
26862         if(this.buttons){
26863             this.add.apply(this, this.buttons);
26864             delete this.buttons;
26865         }
26866     },
26867
26868     /**
26869      * Adds element(s) to the toolbar -- this function takes a variable number of 
26870      * arguments of mixed type and adds them to the toolbar.
26871      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26872      * <ul>
26873      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26874      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26875      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26876      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26877      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26878      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26879      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26880      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26881      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26882      * </ul>
26883      * @param {Mixed} arg2
26884      * @param {Mixed} etc.
26885      */
26886     add : function(){
26887         var a = arguments, l = a.length;
26888         for(var i = 0; i < l; i++){
26889             this._add(a[i]);
26890         }
26891     },
26892     // private..
26893     _add : function(el) {
26894         
26895         if (el.xtype) {
26896             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26897         }
26898         
26899         if (el.applyTo){ // some kind of form field
26900             return this.addField(el);
26901         } 
26902         if (el.render){ // some kind of Toolbar.Item
26903             return this.addItem(el);
26904         }
26905         if (typeof el == "string"){ // string
26906             if(el == "separator" || el == "-"){
26907                 return this.addSeparator();
26908             }
26909             if (el == " "){
26910                 return this.addSpacer();
26911             }
26912             if(el == "->"){
26913                 return this.addFill();
26914             }
26915             return this.addText(el);
26916             
26917         }
26918         if(el.tagName){ // element
26919             return this.addElement(el);
26920         }
26921         if(typeof el == "object"){ // must be button config?
26922             return this.addButton(el);
26923         }
26924         // and now what?!?!
26925         return false;
26926         
26927     },
26928     
26929     /**
26930      * Add an Xtype element
26931      * @param {Object} xtype Xtype Object
26932      * @return {Object} created Object
26933      */
26934     addxtype : function(e){
26935         return this.add(e);  
26936     },
26937     
26938     /**
26939      * Returns the Element for this toolbar.
26940      * @return {Roo.Element}
26941      */
26942     getEl : function(){
26943         return this.el;  
26944     },
26945     
26946     /**
26947      * Adds a separator
26948      * @return {Roo.Toolbar.Item} The separator item
26949      */
26950     addSeparator : function(){
26951         return this.addItem(new Roo.Toolbar.Separator());
26952     },
26953
26954     /**
26955      * Adds a spacer element
26956      * @return {Roo.Toolbar.Spacer} The spacer item
26957      */
26958     addSpacer : function(){
26959         return this.addItem(new Roo.Toolbar.Spacer());
26960     },
26961
26962     /**
26963      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26964      * @return {Roo.Toolbar.Fill} The fill item
26965      */
26966     addFill : function(){
26967         return this.addItem(new Roo.Toolbar.Fill());
26968     },
26969
26970     /**
26971      * Adds any standard HTML element to the toolbar
26972      * @param {String/HTMLElement/Element} el The element or id of the element to add
26973      * @return {Roo.Toolbar.Item} The element's item
26974      */
26975     addElement : function(el){
26976         return this.addItem(new Roo.Toolbar.Item(el));
26977     },
26978     /**
26979      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26980      * @type Roo.util.MixedCollection  
26981      */
26982     items : false,
26983      
26984     /**
26985      * Adds any Toolbar.Item or subclass
26986      * @param {Roo.Toolbar.Item} item
26987      * @return {Roo.Toolbar.Item} The item
26988      */
26989     addItem : function(item){
26990         var td = this.nextBlock();
26991         item.render(td);
26992         this.items.add(item);
26993         return item;
26994     },
26995     
26996     /**
26997      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26998      * @param {Object/Array} config A button config or array of configs
26999      * @return {Roo.Toolbar.Button/Array}
27000      */
27001     addButton : function(config){
27002         if(config instanceof Array){
27003             var buttons = [];
27004             for(var i = 0, len = config.length; i < len; i++) {
27005                 buttons.push(this.addButton(config[i]));
27006             }
27007             return buttons;
27008         }
27009         var b = config;
27010         if(!(config instanceof Roo.Toolbar.Button)){
27011             b = config.split ?
27012                 new Roo.Toolbar.SplitButton(config) :
27013                 new Roo.Toolbar.Button(config);
27014         }
27015         var td = this.nextBlock();
27016         b.render(td);
27017         this.items.add(b);
27018         return b;
27019     },
27020     
27021     /**
27022      * Adds text to the toolbar
27023      * @param {String} text The text to add
27024      * @return {Roo.Toolbar.Item} The element's item
27025      */
27026     addText : function(text){
27027         return this.addItem(new Roo.Toolbar.TextItem(text));
27028     },
27029     
27030     /**
27031      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27032      * @param {Number} index The index where the item is to be inserted
27033      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27034      * @return {Roo.Toolbar.Button/Item}
27035      */
27036     insertButton : function(index, item){
27037         if(item instanceof Array){
27038             var buttons = [];
27039             for(var i = 0, len = item.length; i < len; i++) {
27040                buttons.push(this.insertButton(index + i, item[i]));
27041             }
27042             return buttons;
27043         }
27044         if (!(item instanceof Roo.Toolbar.Button)){
27045            item = new Roo.Toolbar.Button(item);
27046         }
27047         var td = document.createElement("td");
27048         this.tr.insertBefore(td, this.tr.childNodes[index]);
27049         item.render(td);
27050         this.items.insert(index, item);
27051         return item;
27052     },
27053     
27054     /**
27055      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27056      * @param {Object} config
27057      * @return {Roo.Toolbar.Item} The element's item
27058      */
27059     addDom : function(config, returnEl){
27060         var td = this.nextBlock();
27061         Roo.DomHelper.overwrite(td, config);
27062         var ti = new Roo.Toolbar.Item(td.firstChild);
27063         ti.render(td);
27064         this.items.add(ti);
27065         return ti;
27066     },
27067
27068     /**
27069      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27070      * @type Roo.util.MixedCollection  
27071      */
27072     fields : false,
27073     
27074     /**
27075      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27076      * Note: the field should not have been rendered yet. For a field that has already been
27077      * rendered, use {@link #addElement}.
27078      * @param {Roo.form.Field} field
27079      * @return {Roo.ToolbarItem}
27080      */
27081      
27082       
27083     addField : function(field) {
27084         if (!this.fields) {
27085             var autoId = 0;
27086             this.fields = new Roo.util.MixedCollection(false, function(o){
27087                 return o.id || ("item" + (++autoId));
27088             });
27089
27090         }
27091         
27092         var td = this.nextBlock();
27093         field.render(td);
27094         var ti = new Roo.Toolbar.Item(td.firstChild);
27095         ti.render(td);
27096         this.items.add(ti);
27097         this.fields.add(field);
27098         return ti;
27099     },
27100     /**
27101      * Hide the toolbar
27102      * @method hide
27103      */
27104      
27105       
27106     hide : function()
27107     {
27108         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27109         this.el.child('div').hide();
27110     },
27111     /**
27112      * Show the toolbar
27113      * @method show
27114      */
27115     show : function()
27116     {
27117         this.el.child('div').show();
27118     },
27119       
27120     // private
27121     nextBlock : function(){
27122         var td = document.createElement("td");
27123         this.tr.appendChild(td);
27124         return td;
27125     },
27126
27127     // private
27128     destroy : function(){
27129         if(this.items){ // rendered?
27130             Roo.destroy.apply(Roo, this.items.items);
27131         }
27132         if(this.fields){ // rendered?
27133             Roo.destroy.apply(Roo, this.fields.items);
27134         }
27135         Roo.Element.uncache(this.el, this.tr);
27136     }
27137 };
27138
27139 /**
27140  * @class Roo.Toolbar.Item
27141  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27142  * @constructor
27143  * Creates a new Item
27144  * @param {HTMLElement} el 
27145  */
27146 Roo.Toolbar.Item = function(el){
27147     this.el = Roo.getDom(el);
27148     this.id = Roo.id(this.el);
27149     this.hidden = false;
27150 };
27151
27152 Roo.Toolbar.Item.prototype = {
27153     
27154     /**
27155      * Get this item's HTML Element
27156      * @return {HTMLElement}
27157      */
27158     getEl : function(){
27159        return this.el;  
27160     },
27161
27162     // private
27163     render : function(td){
27164         this.td = td;
27165         td.appendChild(this.el);
27166     },
27167     
27168     /**
27169      * Removes and destroys this item.
27170      */
27171     destroy : function(){
27172         this.td.parentNode.removeChild(this.td);
27173     },
27174     
27175     /**
27176      * Shows this item.
27177      */
27178     show: function(){
27179         this.hidden = false;
27180         this.td.style.display = "";
27181     },
27182     
27183     /**
27184      * Hides this item.
27185      */
27186     hide: function(){
27187         this.hidden = true;
27188         this.td.style.display = "none";
27189     },
27190     
27191     /**
27192      * Convenience function for boolean show/hide.
27193      * @param {Boolean} visible true to show/false to hide
27194      */
27195     setVisible: function(visible){
27196         if(visible) {
27197             this.show();
27198         }else{
27199             this.hide();
27200         }
27201     },
27202     
27203     /**
27204      * Try to focus this item.
27205      */
27206     focus : function(){
27207         Roo.fly(this.el).focus();
27208     },
27209     
27210     /**
27211      * Disables this item.
27212      */
27213     disable : function(){
27214         Roo.fly(this.td).addClass("x-item-disabled");
27215         this.disabled = true;
27216         this.el.disabled = true;
27217     },
27218     
27219     /**
27220      * Enables this item.
27221      */
27222     enable : function(){
27223         Roo.fly(this.td).removeClass("x-item-disabled");
27224         this.disabled = false;
27225         this.el.disabled = false;
27226     }
27227 };
27228
27229
27230 /**
27231  * @class Roo.Toolbar.Separator
27232  * @extends Roo.Toolbar.Item
27233  * A simple toolbar separator class
27234  * @constructor
27235  * Creates a new Separator
27236  */
27237 Roo.Toolbar.Separator = function(){
27238     var s = document.createElement("span");
27239     s.className = "ytb-sep";
27240     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27241 };
27242 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27243     enable:Roo.emptyFn,
27244     disable:Roo.emptyFn,
27245     focus:Roo.emptyFn
27246 });
27247
27248 /**
27249  * @class Roo.Toolbar.Spacer
27250  * @extends Roo.Toolbar.Item
27251  * A simple element that adds extra horizontal space to a toolbar.
27252  * @constructor
27253  * Creates a new Spacer
27254  */
27255 Roo.Toolbar.Spacer = function(){
27256     var s = document.createElement("div");
27257     s.className = "ytb-spacer";
27258     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27259 };
27260 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27261     enable:Roo.emptyFn,
27262     disable:Roo.emptyFn,
27263     focus:Roo.emptyFn
27264 });
27265
27266 /**
27267  * @class Roo.Toolbar.Fill
27268  * @extends Roo.Toolbar.Spacer
27269  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27270  * @constructor
27271  * Creates a new Spacer
27272  */
27273 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27274     // private
27275     render : function(td){
27276         td.style.width = '100%';
27277         Roo.Toolbar.Fill.superclass.render.call(this, td);
27278     }
27279 });
27280
27281 /**
27282  * @class Roo.Toolbar.TextItem
27283  * @extends Roo.Toolbar.Item
27284  * A simple class that renders text directly into a toolbar.
27285  * @constructor
27286  * Creates a new TextItem
27287  * @param {String} text
27288  */
27289 Roo.Toolbar.TextItem = function(text){
27290     if (typeof(text) == 'object') {
27291         text = text.text;
27292     }
27293     var s = document.createElement("span");
27294     s.className = "ytb-text";
27295     s.innerHTML = text;
27296     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27297 };
27298 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27299     enable:Roo.emptyFn,
27300     disable:Roo.emptyFn,
27301     focus:Roo.emptyFn
27302 });
27303
27304 /**
27305  * @class Roo.Toolbar.Button
27306  * @extends Roo.Button
27307  * A button that renders into a toolbar.
27308  * @constructor
27309  * Creates a new Button
27310  * @param {Object} config A standard {@link Roo.Button} config object
27311  */
27312 Roo.Toolbar.Button = function(config){
27313     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27314 };
27315 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27316     render : function(td){
27317         this.td = td;
27318         Roo.Toolbar.Button.superclass.render.call(this, td);
27319     },
27320     
27321     /**
27322      * Removes and destroys this button
27323      */
27324     destroy : function(){
27325         Roo.Toolbar.Button.superclass.destroy.call(this);
27326         this.td.parentNode.removeChild(this.td);
27327     },
27328     
27329     /**
27330      * Shows this button
27331      */
27332     show: function(){
27333         this.hidden = false;
27334         this.td.style.display = "";
27335     },
27336     
27337     /**
27338      * Hides this button
27339      */
27340     hide: function(){
27341         this.hidden = true;
27342         this.td.style.display = "none";
27343     },
27344
27345     /**
27346      * Disables this item
27347      */
27348     disable : function(){
27349         Roo.fly(this.td).addClass("x-item-disabled");
27350         this.disabled = true;
27351     },
27352
27353     /**
27354      * Enables this item
27355      */
27356     enable : function(){
27357         Roo.fly(this.td).removeClass("x-item-disabled");
27358         this.disabled = false;
27359     }
27360 });
27361 // backwards compat
27362 Roo.ToolbarButton = Roo.Toolbar.Button;
27363
27364 /**
27365  * @class Roo.Toolbar.SplitButton
27366  * @extends Roo.SplitButton
27367  * A menu button that renders into a toolbar.
27368  * @constructor
27369  * Creates a new SplitButton
27370  * @param {Object} config A standard {@link Roo.SplitButton} config object
27371  */
27372 Roo.Toolbar.SplitButton = function(config){
27373     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27374 };
27375 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27376     render : function(td){
27377         this.td = td;
27378         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27379     },
27380     
27381     /**
27382      * Removes and destroys this button
27383      */
27384     destroy : function(){
27385         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27386         this.td.parentNode.removeChild(this.td);
27387     },
27388     
27389     /**
27390      * Shows this button
27391      */
27392     show: function(){
27393         this.hidden = false;
27394         this.td.style.display = "";
27395     },
27396     
27397     /**
27398      * Hides this button
27399      */
27400     hide: function(){
27401         this.hidden = true;
27402         this.td.style.display = "none";
27403     }
27404 });
27405
27406 // backwards compat
27407 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27408  * Based on:
27409  * Ext JS Library 1.1.1
27410  * Copyright(c) 2006-2007, Ext JS, LLC.
27411  *
27412  * Originally Released Under LGPL - original licence link has changed is not relivant.
27413  *
27414  * Fork - LGPL
27415  * <script type="text/javascript">
27416  */
27417  
27418 /**
27419  * @class Roo.PagingToolbar
27420  * @extends Roo.Toolbar
27421  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27422  * @constructor
27423  * Create a new PagingToolbar
27424  * @param {Object} config The config object
27425  */
27426 Roo.PagingToolbar = function(el, ds, config)
27427 {
27428     // old args format still supported... - xtype is prefered..
27429     if (typeof(el) == 'object' && el.xtype) {
27430         // created from xtype...
27431         config = el;
27432         ds = el.dataSource;
27433         el = config.container;
27434     }
27435     var items = [];
27436     if (config.items) {
27437         items = config.items;
27438         config.items = [];
27439     }
27440     
27441     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27442     this.ds = ds;
27443     this.cursor = 0;
27444     this.renderButtons(this.el);
27445     this.bind(ds);
27446     
27447     // supprot items array.
27448    
27449     Roo.each(items, function(e) {
27450         this.add(Roo.factory(e));
27451     },this);
27452     
27453 };
27454
27455 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27456     /**
27457      * @cfg {Roo.data.Store} dataSource
27458      * The underlying data store providing the paged data
27459      */
27460     /**
27461      * @cfg {String/HTMLElement/Element} container
27462      * container The id or element that will contain the toolbar
27463      */
27464     /**
27465      * @cfg {Boolean} displayInfo
27466      * True to display the displayMsg (defaults to false)
27467      */
27468     /**
27469      * @cfg {Number} pageSize
27470      * The number of records to display per page (defaults to 20)
27471      */
27472     pageSize: 20,
27473     /**
27474      * @cfg {String} displayMsg
27475      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27476      */
27477     displayMsg : 'Displaying {0} - {1} of {2}',
27478     /**
27479      * @cfg {String} emptyMsg
27480      * The message to display when no records are found (defaults to "No data to display")
27481      */
27482     emptyMsg : 'No data to display',
27483     /**
27484      * Customizable piece of the default paging text (defaults to "Page")
27485      * @type String
27486      */
27487     beforePageText : "Page",
27488     /**
27489      * Customizable piece of the default paging text (defaults to "of %0")
27490      * @type String
27491      */
27492     afterPageText : "of {0}",
27493     /**
27494      * Customizable piece of the default paging text (defaults to "First Page")
27495      * @type String
27496      */
27497     firstText : "First Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "Previous Page")
27500      * @type String
27501      */
27502     prevText : "Previous Page",
27503     /**
27504      * Customizable piece of the default paging text (defaults to "Next Page")
27505      * @type String
27506      */
27507     nextText : "Next Page",
27508     /**
27509      * Customizable piece of the default paging text (defaults to "Last Page")
27510      * @type String
27511      */
27512     lastText : "Last Page",
27513     /**
27514      * Customizable piece of the default paging text (defaults to "Refresh")
27515      * @type String
27516      */
27517     refreshText : "Refresh",
27518
27519     // private
27520     renderButtons : function(el){
27521         Roo.PagingToolbar.superclass.render.call(this, el);
27522         this.first = this.addButton({
27523             tooltip: this.firstText,
27524             cls: "x-btn-icon x-grid-page-first",
27525             disabled: true,
27526             handler: this.onClick.createDelegate(this, ["first"])
27527         });
27528         this.prev = this.addButton({
27529             tooltip: this.prevText,
27530             cls: "x-btn-icon x-grid-page-prev",
27531             disabled: true,
27532             handler: this.onClick.createDelegate(this, ["prev"])
27533         });
27534         //this.addSeparator();
27535         this.add(this.beforePageText);
27536         this.field = Roo.get(this.addDom({
27537            tag: "input",
27538            type: "text",
27539            size: "3",
27540            value: "1",
27541            cls: "x-grid-page-number"
27542         }).el);
27543         this.field.on("keydown", this.onPagingKeydown, this);
27544         this.field.on("focus", function(){this.dom.select();});
27545         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27546         this.field.setHeight(18);
27547         //this.addSeparator();
27548         this.next = this.addButton({
27549             tooltip: this.nextText,
27550             cls: "x-btn-icon x-grid-page-next",
27551             disabled: true,
27552             handler: this.onClick.createDelegate(this, ["next"])
27553         });
27554         this.last = this.addButton({
27555             tooltip: this.lastText,
27556             cls: "x-btn-icon x-grid-page-last",
27557             disabled: true,
27558             handler: this.onClick.createDelegate(this, ["last"])
27559         });
27560         //this.addSeparator();
27561         this.loading = this.addButton({
27562             tooltip: this.refreshText,
27563             cls: "x-btn-icon x-grid-loading",
27564             handler: this.onClick.createDelegate(this, ["refresh"])
27565         });
27566
27567         if(this.displayInfo){
27568             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27569         }
27570     },
27571
27572     // private
27573     updateInfo : function(){
27574         if(this.displayEl){
27575             var count = this.ds.getCount();
27576             var msg = count == 0 ?
27577                 this.emptyMsg :
27578                 String.format(
27579                     this.displayMsg,
27580                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27581                 );
27582             this.displayEl.update(msg);
27583         }
27584     },
27585
27586     // private
27587     onLoad : function(ds, r, o){
27588        this.cursor = o.params ? o.params.start : 0;
27589        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27590
27591        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27592        this.field.dom.value = ap;
27593        this.first.setDisabled(ap == 1);
27594        this.prev.setDisabled(ap == 1);
27595        this.next.setDisabled(ap == ps);
27596        this.last.setDisabled(ap == ps);
27597        this.loading.enable();
27598        this.updateInfo();
27599     },
27600
27601     // private
27602     getPageData : function(){
27603         var total = this.ds.getTotalCount();
27604         return {
27605             total : total,
27606             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27607             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27608         };
27609     },
27610
27611     // private
27612     onLoadError : function(){
27613         this.loading.enable();
27614     },
27615
27616     // private
27617     onPagingKeydown : function(e){
27618         var k = e.getKey();
27619         var d = this.getPageData();
27620         if(k == e.RETURN){
27621             var v = this.field.dom.value, pageNum;
27622             if(!v || isNaN(pageNum = parseInt(v, 10))){
27623                 this.field.dom.value = d.activePage;
27624                 return;
27625             }
27626             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27627             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27628             e.stopEvent();
27629         }
27630         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))
27631         {
27632           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27633           this.field.dom.value = pageNum;
27634           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27635           e.stopEvent();
27636         }
27637         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27638         {
27639           var v = this.field.dom.value, pageNum; 
27640           var increment = (e.shiftKey) ? 10 : 1;
27641           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27642             increment *= -1;
27643           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27644             this.field.dom.value = d.activePage;
27645             return;
27646           }
27647           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27648           {
27649             this.field.dom.value = parseInt(v, 10) + increment;
27650             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27651             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27652           }
27653           e.stopEvent();
27654         }
27655     },
27656
27657     // private
27658     beforeLoad : function(){
27659         if(this.loading){
27660             this.loading.disable();
27661         }
27662     },
27663
27664     // private
27665     onClick : function(which){
27666         var ds = this.ds;
27667         switch(which){
27668             case "first":
27669                 ds.load({params:{start: 0, limit: this.pageSize}});
27670             break;
27671             case "prev":
27672                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27673             break;
27674             case "next":
27675                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27676             break;
27677             case "last":
27678                 var total = ds.getTotalCount();
27679                 var extra = total % this.pageSize;
27680                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27681                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27682             break;
27683             case "refresh":
27684                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27685             break;
27686         }
27687     },
27688
27689     /**
27690      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27691      * @param {Roo.data.Store} store The data store to unbind
27692      */
27693     unbind : function(ds){
27694         ds.un("beforeload", this.beforeLoad, this);
27695         ds.un("load", this.onLoad, this);
27696         ds.un("loadexception", this.onLoadError, this);
27697         ds.un("remove", this.updateInfo, this);
27698         ds.un("add", this.updateInfo, this);
27699         this.ds = undefined;
27700     },
27701
27702     /**
27703      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27704      * @param {Roo.data.Store} store The data store to bind
27705      */
27706     bind : function(ds){
27707         ds.on("beforeload", this.beforeLoad, this);
27708         ds.on("load", this.onLoad, this);
27709         ds.on("loadexception", this.onLoadError, this);
27710         ds.on("remove", this.updateInfo, this);
27711         ds.on("add", this.updateInfo, this);
27712         this.ds = ds;
27713     }
27714 });/*
27715  * Based on:
27716  * Ext JS Library 1.1.1
27717  * Copyright(c) 2006-2007, Ext JS, LLC.
27718  *
27719  * Originally Released Under LGPL - original licence link has changed is not relivant.
27720  *
27721  * Fork - LGPL
27722  * <script type="text/javascript">
27723  */
27724
27725 /**
27726  * @class Roo.Resizable
27727  * @extends Roo.util.Observable
27728  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27729  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27730  * 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
27731  * the element will be wrapped for you automatically.</p>
27732  * <p>Here is the list of valid resize handles:</p>
27733  * <pre>
27734 Value   Description
27735 ------  -------------------
27736  'n'     north
27737  's'     south
27738  'e'     east
27739  'w'     west
27740  'nw'    northwest
27741  'sw'    southwest
27742  'se'    southeast
27743  'ne'    northeast
27744  'hd'    horizontal drag
27745  'all'   all
27746 </pre>
27747  * <p>Here's an example showing the creation of a typical Resizable:</p>
27748  * <pre><code>
27749 var resizer = new Roo.Resizable("element-id", {
27750     handles: 'all',
27751     minWidth: 200,
27752     minHeight: 100,
27753     maxWidth: 500,
27754     maxHeight: 400,
27755     pinned: true
27756 });
27757 resizer.on("resize", myHandler);
27758 </code></pre>
27759  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27760  * resizer.east.setDisplayed(false);</p>
27761  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27762  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27763  * resize operation's new size (defaults to [0, 0])
27764  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27765  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27766  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27767  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27768  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27769  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27770  * @cfg {Number} width The width of the element in pixels (defaults to null)
27771  * @cfg {Number} height The height of the element in pixels (defaults to null)
27772  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27773  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27774  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27775  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27776  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27777  * in favor of the handles config option (defaults to false)
27778  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27779  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27780  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27781  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27782  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27783  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27784  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27785  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27786  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27787  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27788  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27789  * @constructor
27790  * Create a new resizable component
27791  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27792  * @param {Object} config configuration options
27793   */
27794 Roo.Resizable = function(el, config)
27795 {
27796     this.el = Roo.get(el);
27797
27798     if(config && config.wrap){
27799         config.resizeChild = this.el;
27800         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27801         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27802         this.el.setStyle("overflow", "hidden");
27803         this.el.setPositioning(config.resizeChild.getPositioning());
27804         config.resizeChild.clearPositioning();
27805         if(!config.width || !config.height){
27806             var csize = config.resizeChild.getSize();
27807             this.el.setSize(csize.width, csize.height);
27808         }
27809         if(config.pinned && !config.adjustments){
27810             config.adjustments = "auto";
27811         }
27812     }
27813
27814     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27815     this.proxy.unselectable();
27816     this.proxy.enableDisplayMode('block');
27817
27818     Roo.apply(this, config);
27819
27820     if(this.pinned){
27821         this.disableTrackOver = true;
27822         this.el.addClass("x-resizable-pinned");
27823     }
27824     // if the element isn't positioned, make it relative
27825     var position = this.el.getStyle("position");
27826     if(position != "absolute" && position != "fixed"){
27827         this.el.setStyle("position", "relative");
27828     }
27829     if(!this.handles){ // no handles passed, must be legacy style
27830         this.handles = 's,e,se';
27831         if(this.multiDirectional){
27832             this.handles += ',n,w';
27833         }
27834     }
27835     if(this.handles == "all"){
27836         this.handles = "n s e w ne nw se sw";
27837     }
27838     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27839     var ps = Roo.Resizable.positions;
27840     for(var i = 0, len = hs.length; i < len; i++){
27841         if(hs[i] && ps[hs[i]]){
27842             var pos = ps[hs[i]];
27843             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27844         }
27845     }
27846     // legacy
27847     this.corner = this.southeast;
27848     
27849     // updateBox = the box can move..
27850     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27851         this.updateBox = true;
27852     }
27853
27854     this.activeHandle = null;
27855
27856     if(this.resizeChild){
27857         if(typeof this.resizeChild == "boolean"){
27858             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27859         }else{
27860             this.resizeChild = Roo.get(this.resizeChild, true);
27861         }
27862     }
27863     
27864     if(this.adjustments == "auto"){
27865         var rc = this.resizeChild;
27866         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27867         if(rc && (hw || hn)){
27868             rc.position("relative");
27869             rc.setLeft(hw ? hw.el.getWidth() : 0);
27870             rc.setTop(hn ? hn.el.getHeight() : 0);
27871         }
27872         this.adjustments = [
27873             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27874             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27875         ];
27876     }
27877
27878     if(this.draggable){
27879         this.dd = this.dynamic ?
27880             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27881         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27882     }
27883
27884     // public events
27885     this.addEvents({
27886         /**
27887          * @event beforeresize
27888          * Fired before resize is allowed. Set enabled to false to cancel resize.
27889          * @param {Roo.Resizable} this
27890          * @param {Roo.EventObject} e The mousedown event
27891          */
27892         "beforeresize" : true,
27893         /**
27894          * @event resize
27895          * Fired after a resize.
27896          * @param {Roo.Resizable} this
27897          * @param {Number} width The new width
27898          * @param {Number} height The new height
27899          * @param {Roo.EventObject} e The mouseup event
27900          */
27901         "resize" : true
27902     });
27903
27904     if(this.width !== null && this.height !== null){
27905         this.resizeTo(this.width, this.height);
27906     }else{
27907         this.updateChildSize();
27908     }
27909     if(Roo.isIE){
27910         this.el.dom.style.zoom = 1;
27911     }
27912     Roo.Resizable.superclass.constructor.call(this);
27913 };
27914
27915 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27916         resizeChild : false,
27917         adjustments : [0, 0],
27918         minWidth : 5,
27919         minHeight : 5,
27920         maxWidth : 10000,
27921         maxHeight : 10000,
27922         enabled : true,
27923         animate : false,
27924         duration : .35,
27925         dynamic : false,
27926         handles : false,
27927         multiDirectional : false,
27928         disableTrackOver : false,
27929         easing : 'easeOutStrong',
27930         widthIncrement : 0,
27931         heightIncrement : 0,
27932         pinned : false,
27933         width : null,
27934         height : null,
27935         preserveRatio : false,
27936         transparent: false,
27937         minX: 0,
27938         minY: 0,
27939         draggable: false,
27940
27941         /**
27942          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27943          */
27944         constrainTo: undefined,
27945         /**
27946          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27947          */
27948         resizeRegion: undefined,
27949
27950
27951     /**
27952      * Perform a manual resize
27953      * @param {Number} width
27954      * @param {Number} height
27955      */
27956     resizeTo : function(width, height){
27957         this.el.setSize(width, height);
27958         this.updateChildSize();
27959         this.fireEvent("resize", this, width, height, null);
27960     },
27961
27962     // private
27963     startSizing : function(e, handle){
27964         this.fireEvent("beforeresize", this, e);
27965         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27966
27967             if(!this.overlay){
27968                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27969                 this.overlay.unselectable();
27970                 this.overlay.enableDisplayMode("block");
27971                 this.overlay.on("mousemove", this.onMouseMove, this);
27972                 this.overlay.on("mouseup", this.onMouseUp, this);
27973             }
27974             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27975
27976             this.resizing = true;
27977             this.startBox = this.el.getBox();
27978             this.startPoint = e.getXY();
27979             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27980                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27981
27982             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27983             this.overlay.show();
27984
27985             if(this.constrainTo) {
27986                 var ct = Roo.get(this.constrainTo);
27987                 this.resizeRegion = ct.getRegion().adjust(
27988                     ct.getFrameWidth('t'),
27989                     ct.getFrameWidth('l'),
27990                     -ct.getFrameWidth('b'),
27991                     -ct.getFrameWidth('r')
27992                 );
27993             }
27994
27995             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27996             this.proxy.show();
27997             this.proxy.setBox(this.startBox);
27998             if(!this.dynamic){
27999                 this.proxy.setStyle('visibility', 'visible');
28000             }
28001         }
28002     },
28003
28004     // private
28005     onMouseDown : function(handle, e){
28006         if(this.enabled){
28007             e.stopEvent();
28008             this.activeHandle = handle;
28009             this.startSizing(e, handle);
28010         }
28011     },
28012
28013     // private
28014     onMouseUp : function(e){
28015         var size = this.resizeElement();
28016         this.resizing = false;
28017         this.handleOut();
28018         this.overlay.hide();
28019         this.proxy.hide();
28020         this.fireEvent("resize", this, size.width, size.height, e);
28021     },
28022
28023     // private
28024     updateChildSize : function(){
28025         if(this.resizeChild){
28026             var el = this.el;
28027             var child = this.resizeChild;
28028             var adj = this.adjustments;
28029             if(el.dom.offsetWidth){
28030                 var b = el.getSize(true);
28031                 child.setSize(b.width+adj[0], b.height+adj[1]);
28032             }
28033             // Second call here for IE
28034             // The first call enables instant resizing and
28035             // the second call corrects scroll bars if they
28036             // exist
28037             if(Roo.isIE){
28038                 setTimeout(function(){
28039                     if(el.dom.offsetWidth){
28040                         var b = el.getSize(true);
28041                         child.setSize(b.width+adj[0], b.height+adj[1]);
28042                     }
28043                 }, 10);
28044             }
28045         }
28046     },
28047
28048     // private
28049     snap : function(value, inc, min){
28050         if(!inc || !value) return value;
28051         var newValue = value;
28052         var m = value % inc;
28053         if(m > 0){
28054             if(m > (inc/2)){
28055                 newValue = value + (inc-m);
28056             }else{
28057                 newValue = value - m;
28058             }
28059         }
28060         return Math.max(min, newValue);
28061     },
28062
28063     // private
28064     resizeElement : function(){
28065         var box = this.proxy.getBox();
28066         if(this.updateBox){
28067             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28068         }else{
28069             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28070         }
28071         this.updateChildSize();
28072         if(!this.dynamic){
28073             this.proxy.hide();
28074         }
28075         return box;
28076     },
28077
28078     // private
28079     constrain : function(v, diff, m, mx){
28080         if(v - diff < m){
28081             diff = v - m;
28082         }else if(v - diff > mx){
28083             diff = mx - v;
28084         }
28085         return diff;
28086     },
28087
28088     // private
28089     onMouseMove : function(e){
28090         if(this.enabled){
28091             try{// try catch so if something goes wrong the user doesn't get hung
28092
28093             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28094                 return;
28095             }
28096
28097             //var curXY = this.startPoint;
28098             var curSize = this.curSize || this.startBox;
28099             var x = this.startBox.x, y = this.startBox.y;
28100             var ox = x, oy = y;
28101             var w = curSize.width, h = curSize.height;
28102             var ow = w, oh = h;
28103             var mw = this.minWidth, mh = this.minHeight;
28104             var mxw = this.maxWidth, mxh = this.maxHeight;
28105             var wi = this.widthIncrement;
28106             var hi = this.heightIncrement;
28107
28108             var eventXY = e.getXY();
28109             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28110             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28111
28112             var pos = this.activeHandle.position;
28113
28114             switch(pos){
28115                 case "east":
28116                     w += diffX;
28117                     w = Math.min(Math.max(mw, w), mxw);
28118                     break;
28119              
28120                 case "south":
28121                     h += diffY;
28122                     h = Math.min(Math.max(mh, h), mxh);
28123                     break;
28124                 case "southeast":
28125                     w += diffX;
28126                     h += diffY;
28127                     w = Math.min(Math.max(mw, w), mxw);
28128                     h = Math.min(Math.max(mh, h), mxh);
28129                     break;
28130                 case "north":
28131                     diffY = this.constrain(h, diffY, mh, mxh);
28132                     y += diffY;
28133                     h -= diffY;
28134                     break;
28135                 case "hdrag":
28136                     
28137                     if (wi) {
28138                         var adiffX = Math.abs(diffX);
28139                         var sub = (adiffX % wi); // how much 
28140                         if (sub > (wi/2)) { // far enough to snap
28141                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28142                         } else {
28143                             // remove difference.. 
28144                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28145                         }
28146                     }
28147                     x += diffX;
28148                     x = Math.max(this.minX, x);
28149                     break;
28150                 case "west":
28151                     diffX = this.constrain(w, diffX, mw, mxw);
28152                     x += diffX;
28153                     w -= diffX;
28154                     break;
28155                 case "northeast":
28156                     w += diffX;
28157                     w = Math.min(Math.max(mw, w), mxw);
28158                     diffY = this.constrain(h, diffY, mh, mxh);
28159                     y += diffY;
28160                     h -= diffY;
28161                     break;
28162                 case "northwest":
28163                     diffX = this.constrain(w, diffX, mw, mxw);
28164                     diffY = this.constrain(h, diffY, mh, mxh);
28165                     y += diffY;
28166                     h -= diffY;
28167                     x += diffX;
28168                     w -= diffX;
28169                     break;
28170                case "southwest":
28171                     diffX = this.constrain(w, diffX, mw, mxw);
28172                     h += diffY;
28173                     h = Math.min(Math.max(mh, h), mxh);
28174                     x += diffX;
28175                     w -= diffX;
28176                     break;
28177             }
28178
28179             var sw = this.snap(w, wi, mw);
28180             var sh = this.snap(h, hi, mh);
28181             if(sw != w || sh != h){
28182                 switch(pos){
28183                     case "northeast":
28184                         y -= sh - h;
28185                     break;
28186                     case "north":
28187                         y -= sh - h;
28188                         break;
28189                     case "southwest":
28190                         x -= sw - w;
28191                     break;
28192                     case "west":
28193                         x -= sw - w;
28194                         break;
28195                     case "northwest":
28196                         x -= sw - w;
28197                         y -= sh - h;
28198                     break;
28199                 }
28200                 w = sw;
28201                 h = sh;
28202             }
28203
28204             if(this.preserveRatio){
28205                 switch(pos){
28206                     case "southeast":
28207                     case "east":
28208                         h = oh * (w/ow);
28209                         h = Math.min(Math.max(mh, h), mxh);
28210                         w = ow * (h/oh);
28211                        break;
28212                     case "south":
28213                         w = ow * (h/oh);
28214                         w = Math.min(Math.max(mw, w), mxw);
28215                         h = oh * (w/ow);
28216                         break;
28217                     case "northeast":
28218                         w = ow * (h/oh);
28219                         w = Math.min(Math.max(mw, w), mxw);
28220                         h = oh * (w/ow);
28221                     break;
28222                     case "north":
28223                         var tw = w;
28224                         w = ow * (h/oh);
28225                         w = Math.min(Math.max(mw, w), mxw);
28226                         h = oh * (w/ow);
28227                         x += (tw - w) / 2;
28228                         break;
28229                     case "southwest":
28230                         h = oh * (w/ow);
28231                         h = Math.min(Math.max(mh, h), mxh);
28232                         var tw = w;
28233                         w = ow * (h/oh);
28234                         x += tw - w;
28235                         break;
28236                     case "west":
28237                         var th = h;
28238                         h = oh * (w/ow);
28239                         h = Math.min(Math.max(mh, h), mxh);
28240                         y += (th - h) / 2;
28241                         var tw = w;
28242                         w = ow * (h/oh);
28243                         x += tw - w;
28244                        break;
28245                     case "northwest":
28246                         var tw = w;
28247                         var th = h;
28248                         h = oh * (w/ow);
28249                         h = Math.min(Math.max(mh, h), mxh);
28250                         w = ow * (h/oh);
28251                         y += th - h;
28252                         x += tw - w;
28253                        break;
28254
28255                 }
28256             }
28257             if (pos == 'hdrag') {
28258                 w = ow;
28259             }
28260             this.proxy.setBounds(x, y, w, h);
28261             if(this.dynamic){
28262                 this.resizeElement();
28263             }
28264             }catch(e){}
28265         }
28266     },
28267
28268     // private
28269     handleOver : function(){
28270         if(this.enabled){
28271             this.el.addClass("x-resizable-over");
28272         }
28273     },
28274
28275     // private
28276     handleOut : function(){
28277         if(!this.resizing){
28278             this.el.removeClass("x-resizable-over");
28279         }
28280     },
28281
28282     /**
28283      * Returns the element this component is bound to.
28284      * @return {Roo.Element}
28285      */
28286     getEl : function(){
28287         return this.el;
28288     },
28289
28290     /**
28291      * Returns the resizeChild element (or null).
28292      * @return {Roo.Element}
28293      */
28294     getResizeChild : function(){
28295         return this.resizeChild;
28296     },
28297
28298     /**
28299      * Destroys this resizable. If the element was wrapped and
28300      * removeEl is not true then the element remains.
28301      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28302      */
28303     destroy : function(removeEl){
28304         this.proxy.remove();
28305         if(this.overlay){
28306             this.overlay.removeAllListeners();
28307             this.overlay.remove();
28308         }
28309         var ps = Roo.Resizable.positions;
28310         for(var k in ps){
28311             if(typeof ps[k] != "function" && this[ps[k]]){
28312                 var h = this[ps[k]];
28313                 h.el.removeAllListeners();
28314                 h.el.remove();
28315             }
28316         }
28317         if(removeEl){
28318             this.el.update("");
28319             this.el.remove();
28320         }
28321     }
28322 });
28323
28324 // private
28325 // hash to map config positions to true positions
28326 Roo.Resizable.positions = {
28327     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28328     hd: "hdrag"
28329 };
28330
28331 // private
28332 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28333     if(!this.tpl){
28334         // only initialize the template if resizable is used
28335         var tpl = Roo.DomHelper.createTemplate(
28336             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28337         );
28338         tpl.compile();
28339         Roo.Resizable.Handle.prototype.tpl = tpl;
28340     }
28341     this.position = pos;
28342     this.rz = rz;
28343     // show north drag fro topdra
28344     var handlepos = pos == 'hdrag' ? 'north' : pos;
28345     
28346     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28347     if (pos == 'hdrag') {
28348         this.el.setStyle('cursor', 'pointer');
28349     }
28350     this.el.unselectable();
28351     if(transparent){
28352         this.el.setOpacity(0);
28353     }
28354     this.el.on("mousedown", this.onMouseDown, this);
28355     if(!disableTrackOver){
28356         this.el.on("mouseover", this.onMouseOver, this);
28357         this.el.on("mouseout", this.onMouseOut, this);
28358     }
28359 };
28360
28361 // private
28362 Roo.Resizable.Handle.prototype = {
28363     afterResize : function(rz){
28364         // do nothing
28365     },
28366     // private
28367     onMouseDown : function(e){
28368         this.rz.onMouseDown(this, e);
28369     },
28370     // private
28371     onMouseOver : function(e){
28372         this.rz.handleOver(this, e);
28373     },
28374     // private
28375     onMouseOut : function(e){
28376         this.rz.handleOut(this, e);
28377     }
28378 };/*
28379  * Based on:
28380  * Ext JS Library 1.1.1
28381  * Copyright(c) 2006-2007, Ext JS, LLC.
28382  *
28383  * Originally Released Under LGPL - original licence link has changed is not relivant.
28384  *
28385  * Fork - LGPL
28386  * <script type="text/javascript">
28387  */
28388
28389 /**
28390  * @class Roo.Editor
28391  * @extends Roo.Component
28392  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28393  * @constructor
28394  * Create a new Editor
28395  * @param {Roo.form.Field} field The Field object (or descendant)
28396  * @param {Object} config The config object
28397  */
28398 Roo.Editor = function(field, config){
28399     Roo.Editor.superclass.constructor.call(this, config);
28400     this.field = field;
28401     this.addEvents({
28402         /**
28403              * @event beforestartedit
28404              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28405              * false from the handler of this event.
28406              * @param {Editor} this
28407              * @param {Roo.Element} boundEl The underlying element bound to this editor
28408              * @param {Mixed} value The field value being set
28409              */
28410         "beforestartedit" : true,
28411         /**
28412              * @event startedit
28413              * Fires when this editor is displayed
28414              * @param {Roo.Element} boundEl The underlying element bound to this editor
28415              * @param {Mixed} value The starting field value
28416              */
28417         "startedit" : true,
28418         /**
28419              * @event beforecomplete
28420              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28421              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28422              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28423              * event will not fire since no edit actually occurred.
28424              * @param {Editor} this
28425              * @param {Mixed} value The current field value
28426              * @param {Mixed} startValue The original field value
28427              */
28428         "beforecomplete" : true,
28429         /**
28430              * @event complete
28431              * Fires after editing is complete and any changed value has been written to the underlying field.
28432              * @param {Editor} this
28433              * @param {Mixed} value The current field value
28434              * @param {Mixed} startValue The original field value
28435              */
28436         "complete" : true,
28437         /**
28438          * @event specialkey
28439          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28440          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28441          * @param {Roo.form.Field} this
28442          * @param {Roo.EventObject} e The event object
28443          */
28444         "specialkey" : true
28445     });
28446 };
28447
28448 Roo.extend(Roo.Editor, Roo.Component, {
28449     /**
28450      * @cfg {Boolean/String} autosize
28451      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28452      * or "height" to adopt the height only (defaults to false)
28453      */
28454     /**
28455      * @cfg {Boolean} revertInvalid
28456      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28457      * validation fails (defaults to true)
28458      */
28459     /**
28460      * @cfg {Boolean} ignoreNoChange
28461      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28462      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28463      * will never be ignored.
28464      */
28465     /**
28466      * @cfg {Boolean} hideEl
28467      * False to keep the bound element visible while the editor is displayed (defaults to true)
28468      */
28469     /**
28470      * @cfg {Mixed} value
28471      * The data value of the underlying field (defaults to "")
28472      */
28473     value : "",
28474     /**
28475      * @cfg {String} alignment
28476      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28477      */
28478     alignment: "c-c?",
28479     /**
28480      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28481      * for bottom-right shadow (defaults to "frame")
28482      */
28483     shadow : "frame",
28484     /**
28485      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28486      */
28487     constrain : false,
28488     /**
28489      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28490      */
28491     completeOnEnter : false,
28492     /**
28493      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28494      */
28495     cancelOnEsc : false,
28496     /**
28497      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28498      */
28499     updateEl : false,
28500
28501     // private
28502     onRender : function(ct, position){
28503         this.el = new Roo.Layer({
28504             shadow: this.shadow,
28505             cls: "x-editor",
28506             parentEl : ct,
28507             shim : this.shim,
28508             shadowOffset:4,
28509             id: this.id,
28510             constrain: this.constrain
28511         });
28512         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28513         if(this.field.msgTarget != 'title'){
28514             this.field.msgTarget = 'qtip';
28515         }
28516         this.field.render(this.el);
28517         if(Roo.isGecko){
28518             this.field.el.dom.setAttribute('autocomplete', 'off');
28519         }
28520         this.field.on("specialkey", this.onSpecialKey, this);
28521         if(this.swallowKeys){
28522             this.field.el.swallowEvent(['keydown','keypress']);
28523         }
28524         this.field.show();
28525         this.field.on("blur", this.onBlur, this);
28526         if(this.field.grow){
28527             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28528         }
28529     },
28530
28531     onSpecialKey : function(field, e)
28532     {
28533         //Roo.log('editor onSpecialKey');
28534         if(this.completeOnEnter && e.getKey() == e.ENTER){
28535             e.stopEvent();
28536             this.completeEdit();
28537             return;
28538         }
28539         // do not fire special key otherwise it might hide close the editor...
28540         if(e.getKey() == e.ENTER){    
28541             return;
28542         }
28543         if(this.cancelOnEsc && e.getKey() == e.ESC){
28544             this.cancelEdit();
28545             return;
28546         } 
28547         this.fireEvent('specialkey', field, e);
28548     
28549     },
28550
28551     /**
28552      * Starts the editing process and shows the editor.
28553      * @param {String/HTMLElement/Element} el The element to edit
28554      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28555       * to the innerHTML of el.
28556      */
28557     startEdit : function(el, value){
28558         if(this.editing){
28559             this.completeEdit();
28560         }
28561         this.boundEl = Roo.get(el);
28562         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28563         if(!this.rendered){
28564             this.render(this.parentEl || document.body);
28565         }
28566         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28567             return;
28568         }
28569         this.startValue = v;
28570         this.field.setValue(v);
28571         if(this.autoSize){
28572             var sz = this.boundEl.getSize();
28573             switch(this.autoSize){
28574                 case "width":
28575                 this.setSize(sz.width,  "");
28576                 break;
28577                 case "height":
28578                 this.setSize("",  sz.height);
28579                 break;
28580                 default:
28581                 this.setSize(sz.width,  sz.height);
28582             }
28583         }
28584         this.el.alignTo(this.boundEl, this.alignment);
28585         this.editing = true;
28586         if(Roo.QuickTips){
28587             Roo.QuickTips.disable();
28588         }
28589         this.show();
28590     },
28591
28592     /**
28593      * Sets the height and width of this editor.
28594      * @param {Number} width The new width
28595      * @param {Number} height The new height
28596      */
28597     setSize : function(w, h){
28598         this.field.setSize(w, h);
28599         if(this.el){
28600             this.el.sync();
28601         }
28602     },
28603
28604     /**
28605      * Realigns the editor to the bound field based on the current alignment config value.
28606      */
28607     realign : function(){
28608         this.el.alignTo(this.boundEl, this.alignment);
28609     },
28610
28611     /**
28612      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28613      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28614      */
28615     completeEdit : function(remainVisible){
28616         if(!this.editing){
28617             return;
28618         }
28619         var v = this.getValue();
28620         if(this.revertInvalid !== false && !this.field.isValid()){
28621             v = this.startValue;
28622             this.cancelEdit(true);
28623         }
28624         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28625             this.editing = false;
28626             this.hide();
28627             return;
28628         }
28629         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28630             this.editing = false;
28631             if(this.updateEl && this.boundEl){
28632                 this.boundEl.update(v);
28633             }
28634             if(remainVisible !== true){
28635                 this.hide();
28636             }
28637             this.fireEvent("complete", this, v, this.startValue);
28638         }
28639     },
28640
28641     // private
28642     onShow : function(){
28643         this.el.show();
28644         if(this.hideEl !== false){
28645             this.boundEl.hide();
28646         }
28647         this.field.show();
28648         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28649             this.fixIEFocus = true;
28650             this.deferredFocus.defer(50, this);
28651         }else{
28652             this.field.focus();
28653         }
28654         this.fireEvent("startedit", this.boundEl, this.startValue);
28655     },
28656
28657     deferredFocus : function(){
28658         if(this.editing){
28659             this.field.focus();
28660         }
28661     },
28662
28663     /**
28664      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28665      * reverted to the original starting value.
28666      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28667      * cancel (defaults to false)
28668      */
28669     cancelEdit : function(remainVisible){
28670         if(this.editing){
28671             this.setValue(this.startValue);
28672             if(remainVisible !== true){
28673                 this.hide();
28674             }
28675         }
28676     },
28677
28678     // private
28679     onBlur : function(){
28680         if(this.allowBlur !== true && this.editing){
28681             this.completeEdit();
28682         }
28683     },
28684
28685     // private
28686     onHide : function(){
28687         if(this.editing){
28688             this.completeEdit();
28689             return;
28690         }
28691         this.field.blur();
28692         if(this.field.collapse){
28693             this.field.collapse();
28694         }
28695         this.el.hide();
28696         if(this.hideEl !== false){
28697             this.boundEl.show();
28698         }
28699         if(Roo.QuickTips){
28700             Roo.QuickTips.enable();
28701         }
28702     },
28703
28704     /**
28705      * Sets the data value of the editor
28706      * @param {Mixed} value Any valid value supported by the underlying field
28707      */
28708     setValue : function(v){
28709         this.field.setValue(v);
28710     },
28711
28712     /**
28713      * Gets the data value of the editor
28714      * @return {Mixed} The data value
28715      */
28716     getValue : function(){
28717         return this.field.getValue();
28718     }
28719 });/*
28720  * Based on:
28721  * Ext JS Library 1.1.1
28722  * Copyright(c) 2006-2007, Ext JS, LLC.
28723  *
28724  * Originally Released Under LGPL - original licence link has changed is not relivant.
28725  *
28726  * Fork - LGPL
28727  * <script type="text/javascript">
28728  */
28729  
28730 /**
28731  * @class Roo.BasicDialog
28732  * @extends Roo.util.Observable
28733  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28734  * <pre><code>
28735 var dlg = new Roo.BasicDialog("my-dlg", {
28736     height: 200,
28737     width: 300,
28738     minHeight: 100,
28739     minWidth: 150,
28740     modal: true,
28741     proxyDrag: true,
28742     shadow: true
28743 });
28744 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28745 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28746 dlg.addButton('Cancel', dlg.hide, dlg);
28747 dlg.show();
28748 </code></pre>
28749   <b>A Dialog should always be a direct child of the body element.</b>
28750  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28751  * @cfg {String} title Default text to display in the title bar (defaults to null)
28752  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28753  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28754  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28755  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28756  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28757  * (defaults to null with no animation)
28758  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28759  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28760  * property for valid values (defaults to 'all')
28761  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28762  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28763  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28764  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28765  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28766  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28767  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28768  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28769  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28770  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28771  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28772  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28773  * draggable = true (defaults to false)
28774  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28775  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28776  * shadow (defaults to false)
28777  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28778  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28779  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28780  * @cfg {Array} buttons Array of buttons
28781  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28782  * @constructor
28783  * Create a new BasicDialog.
28784  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28785  * @param {Object} config Configuration options
28786  */
28787 Roo.BasicDialog = function(el, config){
28788     this.el = Roo.get(el);
28789     var dh = Roo.DomHelper;
28790     if(!this.el && config && config.autoCreate){
28791         if(typeof config.autoCreate == "object"){
28792             if(!config.autoCreate.id){
28793                 config.autoCreate.id = el;
28794             }
28795             this.el = dh.append(document.body,
28796                         config.autoCreate, true);
28797         }else{
28798             this.el = dh.append(document.body,
28799                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28800         }
28801     }
28802     el = this.el;
28803     el.setDisplayed(true);
28804     el.hide = this.hideAction;
28805     this.id = el.id;
28806     el.addClass("x-dlg");
28807
28808     Roo.apply(this, config);
28809
28810     this.proxy = el.createProxy("x-dlg-proxy");
28811     this.proxy.hide = this.hideAction;
28812     this.proxy.setOpacity(.5);
28813     this.proxy.hide();
28814
28815     if(config.width){
28816         el.setWidth(config.width);
28817     }
28818     if(config.height){
28819         el.setHeight(config.height);
28820     }
28821     this.size = el.getSize();
28822     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28823         this.xy = [config.x,config.y];
28824     }else{
28825         this.xy = el.getCenterXY(true);
28826     }
28827     /** The header element @type Roo.Element */
28828     this.header = el.child("> .x-dlg-hd");
28829     /** The body element @type Roo.Element */
28830     this.body = el.child("> .x-dlg-bd");
28831     /** The footer element @type Roo.Element */
28832     this.footer = el.child("> .x-dlg-ft");
28833
28834     if(!this.header){
28835         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28836     }
28837     if(!this.body){
28838         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28839     }
28840
28841     this.header.unselectable();
28842     if(this.title){
28843         this.header.update(this.title);
28844     }
28845     // this element allows the dialog to be focused for keyboard event
28846     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28847     this.focusEl.swallowEvent("click", true);
28848
28849     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28850
28851     // wrap the body and footer for special rendering
28852     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28853     if(this.footer){
28854         this.bwrap.dom.appendChild(this.footer.dom);
28855     }
28856
28857     this.bg = this.el.createChild({
28858         tag: "div", cls:"x-dlg-bg",
28859         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28860     });
28861     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28862
28863
28864     if(this.autoScroll !== false && !this.autoTabs){
28865         this.body.setStyle("overflow", "auto");
28866     }
28867
28868     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28869
28870     if(this.closable !== false){
28871         this.el.addClass("x-dlg-closable");
28872         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28873         this.close.on("click", this.closeClick, this);
28874         this.close.addClassOnOver("x-dlg-close-over");
28875     }
28876     if(this.collapsible !== false){
28877         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28878         this.collapseBtn.on("click", this.collapseClick, this);
28879         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28880         this.header.on("dblclick", this.collapseClick, this);
28881     }
28882     if(this.resizable !== false){
28883         this.el.addClass("x-dlg-resizable");
28884         this.resizer = new Roo.Resizable(el, {
28885             minWidth: this.minWidth || 80,
28886             minHeight:this.minHeight || 80,
28887             handles: this.resizeHandles || "all",
28888             pinned: true
28889         });
28890         this.resizer.on("beforeresize", this.beforeResize, this);
28891         this.resizer.on("resize", this.onResize, this);
28892     }
28893     if(this.draggable !== false){
28894         el.addClass("x-dlg-draggable");
28895         if (!this.proxyDrag) {
28896             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28897         }
28898         else {
28899             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28900         }
28901         dd.setHandleElId(this.header.id);
28902         dd.endDrag = this.endMove.createDelegate(this);
28903         dd.startDrag = this.startMove.createDelegate(this);
28904         dd.onDrag = this.onDrag.createDelegate(this);
28905         dd.scroll = false;
28906         this.dd = dd;
28907     }
28908     if(this.modal){
28909         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28910         this.mask.enableDisplayMode("block");
28911         this.mask.hide();
28912         this.el.addClass("x-dlg-modal");
28913     }
28914     if(this.shadow){
28915         this.shadow = new Roo.Shadow({
28916             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28917             offset : this.shadowOffset
28918         });
28919     }else{
28920         this.shadowOffset = 0;
28921     }
28922     if(Roo.useShims && this.shim !== false){
28923         this.shim = this.el.createShim();
28924         this.shim.hide = this.hideAction;
28925         this.shim.hide();
28926     }else{
28927         this.shim = false;
28928     }
28929     if(this.autoTabs){
28930         this.initTabs();
28931     }
28932     if (this.buttons) { 
28933         var bts= this.buttons;
28934         this.buttons = [];
28935         Roo.each(bts, function(b) {
28936             this.addButton(b);
28937         }, this);
28938     }
28939     
28940     
28941     this.addEvents({
28942         /**
28943          * @event keydown
28944          * Fires when a key is pressed
28945          * @param {Roo.BasicDialog} this
28946          * @param {Roo.EventObject} e
28947          */
28948         "keydown" : true,
28949         /**
28950          * @event move
28951          * Fires when this dialog is moved by the user.
28952          * @param {Roo.BasicDialog} this
28953          * @param {Number} x The new page X
28954          * @param {Number} y The new page Y
28955          */
28956         "move" : true,
28957         /**
28958          * @event resize
28959          * Fires when this dialog is resized by the user.
28960          * @param {Roo.BasicDialog} this
28961          * @param {Number} width The new width
28962          * @param {Number} height The new height
28963          */
28964         "resize" : true,
28965         /**
28966          * @event beforehide
28967          * Fires before this dialog is hidden.
28968          * @param {Roo.BasicDialog} this
28969          */
28970         "beforehide" : true,
28971         /**
28972          * @event hide
28973          * Fires when this dialog is hidden.
28974          * @param {Roo.BasicDialog} this
28975          */
28976         "hide" : true,
28977         /**
28978          * @event beforeshow
28979          * Fires before this dialog is shown.
28980          * @param {Roo.BasicDialog} this
28981          */
28982         "beforeshow" : true,
28983         /**
28984          * @event show
28985          * Fires when this dialog is shown.
28986          * @param {Roo.BasicDialog} this
28987          */
28988         "show" : true
28989     });
28990     el.on("keydown", this.onKeyDown, this);
28991     el.on("mousedown", this.toFront, this);
28992     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28993     this.el.hide();
28994     Roo.DialogManager.register(this);
28995     Roo.BasicDialog.superclass.constructor.call(this);
28996 };
28997
28998 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28999     shadowOffset: Roo.isIE ? 6 : 5,
29000     minHeight: 80,
29001     minWidth: 200,
29002     minButtonWidth: 75,
29003     defaultButton: null,
29004     buttonAlign: "right",
29005     tabTag: 'div',
29006     firstShow: true,
29007
29008     /**
29009      * Sets the dialog title text
29010      * @param {String} text The title text to display
29011      * @return {Roo.BasicDialog} this
29012      */
29013     setTitle : function(text){
29014         this.header.update(text);
29015         return this;
29016     },
29017
29018     // private
29019     closeClick : function(){
29020         this.hide();
29021     },
29022
29023     // private
29024     collapseClick : function(){
29025         this[this.collapsed ? "expand" : "collapse"]();
29026     },
29027
29028     /**
29029      * Collapses the dialog to its minimized state (only the title bar is visible).
29030      * Equivalent to the user clicking the collapse dialog button.
29031      */
29032     collapse : function(){
29033         if(!this.collapsed){
29034             this.collapsed = true;
29035             this.el.addClass("x-dlg-collapsed");
29036             this.restoreHeight = this.el.getHeight();
29037             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29038         }
29039     },
29040
29041     /**
29042      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29043      * clicking the expand dialog button.
29044      */
29045     expand : function(){
29046         if(this.collapsed){
29047             this.collapsed = false;
29048             this.el.removeClass("x-dlg-collapsed");
29049             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29050         }
29051     },
29052
29053     /**
29054      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29055      * @return {Roo.TabPanel} The tabs component
29056      */
29057     initTabs : function(){
29058         var tabs = this.getTabs();
29059         while(tabs.getTab(0)){
29060             tabs.removeTab(0);
29061         }
29062         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29063             var dom = el.dom;
29064             tabs.addTab(Roo.id(dom), dom.title);
29065             dom.title = "";
29066         });
29067         tabs.activate(0);
29068         return tabs;
29069     },
29070
29071     // private
29072     beforeResize : function(){
29073         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29074     },
29075
29076     // private
29077     onResize : function(){
29078         this.refreshSize();
29079         this.syncBodyHeight();
29080         this.adjustAssets();
29081         this.focus();
29082         this.fireEvent("resize", this, this.size.width, this.size.height);
29083     },
29084
29085     // private
29086     onKeyDown : function(e){
29087         if(this.isVisible()){
29088             this.fireEvent("keydown", this, e);
29089         }
29090     },
29091
29092     /**
29093      * Resizes the dialog.
29094      * @param {Number} width
29095      * @param {Number} height
29096      * @return {Roo.BasicDialog} this
29097      */
29098     resizeTo : function(width, height){
29099         this.el.setSize(width, height);
29100         this.size = {width: width, height: height};
29101         this.syncBodyHeight();
29102         if(this.fixedcenter){
29103             this.center();
29104         }
29105         if(this.isVisible()){
29106             this.constrainXY();
29107             this.adjustAssets();
29108         }
29109         this.fireEvent("resize", this, width, height);
29110         return this;
29111     },
29112
29113
29114     /**
29115      * Resizes the dialog to fit the specified content size.
29116      * @param {Number} width
29117      * @param {Number} height
29118      * @return {Roo.BasicDialog} this
29119      */
29120     setContentSize : function(w, h){
29121         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29122         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29123         //if(!this.el.isBorderBox()){
29124             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29125             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29126         //}
29127         if(this.tabs){
29128             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29129             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29130         }
29131         this.resizeTo(w, h);
29132         return this;
29133     },
29134
29135     /**
29136      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29137      * executed in response to a particular key being pressed while the dialog is active.
29138      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29139      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29140      * @param {Function} fn The function to call
29141      * @param {Object} scope (optional) The scope of the function
29142      * @return {Roo.BasicDialog} this
29143      */
29144     addKeyListener : function(key, fn, scope){
29145         var keyCode, shift, ctrl, alt;
29146         if(typeof key == "object" && !(key instanceof Array)){
29147             keyCode = key["key"];
29148             shift = key["shift"];
29149             ctrl = key["ctrl"];
29150             alt = key["alt"];
29151         }else{
29152             keyCode = key;
29153         }
29154         var handler = function(dlg, e){
29155             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29156                 var k = e.getKey();
29157                 if(keyCode instanceof Array){
29158                     for(var i = 0, len = keyCode.length; i < len; i++){
29159                         if(keyCode[i] == k){
29160                           fn.call(scope || window, dlg, k, e);
29161                           return;
29162                         }
29163                     }
29164                 }else{
29165                     if(k == keyCode){
29166                         fn.call(scope || window, dlg, k, e);
29167                     }
29168                 }
29169             }
29170         };
29171         this.on("keydown", handler);
29172         return this;
29173     },
29174
29175     /**
29176      * Returns the TabPanel component (creates it if it doesn't exist).
29177      * Note: If you wish to simply check for the existence of tabs without creating them,
29178      * check for a null 'tabs' property.
29179      * @return {Roo.TabPanel} The tabs component
29180      */
29181     getTabs : function(){
29182         if(!this.tabs){
29183             this.el.addClass("x-dlg-auto-tabs");
29184             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29185             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29186         }
29187         return this.tabs;
29188     },
29189
29190     /**
29191      * Adds a button to the footer section of the dialog.
29192      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29193      * object or a valid Roo.DomHelper element config
29194      * @param {Function} handler The function called when the button is clicked
29195      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29196      * @return {Roo.Button} The new button
29197      */
29198     addButton : function(config, handler, scope){
29199         var dh = Roo.DomHelper;
29200         if(!this.footer){
29201             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29202         }
29203         if(!this.btnContainer){
29204             var tb = this.footer.createChild({
29205
29206                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29207                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29208             }, null, true);
29209             this.btnContainer = tb.firstChild.firstChild.firstChild;
29210         }
29211         var bconfig = {
29212             handler: handler,
29213             scope: scope,
29214             minWidth: this.minButtonWidth,
29215             hideParent:true
29216         };
29217         if(typeof config == "string"){
29218             bconfig.text = config;
29219         }else{
29220             if(config.tag){
29221                 bconfig.dhconfig = config;
29222             }else{
29223                 Roo.apply(bconfig, config);
29224             }
29225         }
29226         var fc = false;
29227         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29228             bconfig.position = Math.max(0, bconfig.position);
29229             fc = this.btnContainer.childNodes[bconfig.position];
29230         }
29231          
29232         var btn = new Roo.Button(
29233             fc ? 
29234                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29235                 : this.btnContainer.appendChild(document.createElement("td")),
29236             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29237             bconfig
29238         );
29239         this.syncBodyHeight();
29240         if(!this.buttons){
29241             /**
29242              * Array of all the buttons that have been added to this dialog via addButton
29243              * @type Array
29244              */
29245             this.buttons = [];
29246         }
29247         this.buttons.push(btn);
29248         return btn;
29249     },
29250
29251     /**
29252      * Sets the default button to be focused when the dialog is displayed.
29253      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29254      * @return {Roo.BasicDialog} this
29255      */
29256     setDefaultButton : function(btn){
29257         this.defaultButton = btn;
29258         return this;
29259     },
29260
29261     // private
29262     getHeaderFooterHeight : function(safe){
29263         var height = 0;
29264         if(this.header){
29265            height += this.header.getHeight();
29266         }
29267         if(this.footer){
29268            var fm = this.footer.getMargins();
29269             height += (this.footer.getHeight()+fm.top+fm.bottom);
29270         }
29271         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29272         height += this.centerBg.getPadding("tb");
29273         return height;
29274     },
29275
29276     // private
29277     syncBodyHeight : function(){
29278         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29279         var height = this.size.height - this.getHeaderFooterHeight(false);
29280         bd.setHeight(height-bd.getMargins("tb"));
29281         var hh = this.header.getHeight();
29282         var h = this.size.height-hh;
29283         cb.setHeight(h);
29284         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29285         bw.setHeight(h-cb.getPadding("tb"));
29286         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29287         bd.setWidth(bw.getWidth(true));
29288         if(this.tabs){
29289             this.tabs.syncHeight();
29290             if(Roo.isIE){
29291                 this.tabs.el.repaint();
29292             }
29293         }
29294     },
29295
29296     /**
29297      * Restores the previous state of the dialog if Roo.state is configured.
29298      * @return {Roo.BasicDialog} this
29299      */
29300     restoreState : function(){
29301         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29302         if(box && box.width){
29303             this.xy = [box.x, box.y];
29304             this.resizeTo(box.width, box.height);
29305         }
29306         return this;
29307     },
29308
29309     // private
29310     beforeShow : function(){
29311         this.expand();
29312         if(this.fixedcenter){
29313             this.xy = this.el.getCenterXY(true);
29314         }
29315         if(this.modal){
29316             Roo.get(document.body).addClass("x-body-masked");
29317             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29318             this.mask.show();
29319         }
29320         this.constrainXY();
29321     },
29322
29323     // private
29324     animShow : function(){
29325         var b = Roo.get(this.animateTarget).getBox();
29326         this.proxy.setSize(b.width, b.height);
29327         this.proxy.setLocation(b.x, b.y);
29328         this.proxy.show();
29329         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29330                     true, .35, this.showEl.createDelegate(this));
29331     },
29332
29333     /**
29334      * Shows the dialog.
29335      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29336      * @return {Roo.BasicDialog} this
29337      */
29338     show : function(animateTarget){
29339         if (this.fireEvent("beforeshow", this) === false){
29340             return;
29341         }
29342         if(this.syncHeightBeforeShow){
29343             this.syncBodyHeight();
29344         }else if(this.firstShow){
29345             this.firstShow = false;
29346             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29347         }
29348         this.animateTarget = animateTarget || this.animateTarget;
29349         if(!this.el.isVisible()){
29350             this.beforeShow();
29351             if(this.animateTarget && Roo.get(this.animateTarget)){
29352                 this.animShow();
29353             }else{
29354                 this.showEl();
29355             }
29356         }
29357         return this;
29358     },
29359
29360     // private
29361     showEl : function(){
29362         this.proxy.hide();
29363         this.el.setXY(this.xy);
29364         this.el.show();
29365         this.adjustAssets(true);
29366         this.toFront();
29367         this.focus();
29368         // IE peekaboo bug - fix found by Dave Fenwick
29369         if(Roo.isIE){
29370             this.el.repaint();
29371         }
29372         this.fireEvent("show", this);
29373     },
29374
29375     /**
29376      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29377      * dialog itself will receive focus.
29378      */
29379     focus : function(){
29380         if(this.defaultButton){
29381             this.defaultButton.focus();
29382         }else{
29383             this.focusEl.focus();
29384         }
29385     },
29386
29387     // private
29388     constrainXY : function(){
29389         if(this.constraintoviewport !== false){
29390             if(!this.viewSize){
29391                 if(this.container){
29392                     var s = this.container.getSize();
29393                     this.viewSize = [s.width, s.height];
29394                 }else{
29395                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29396                 }
29397             }
29398             var s = Roo.get(this.container||document).getScroll();
29399
29400             var x = this.xy[0], y = this.xy[1];
29401             var w = this.size.width, h = this.size.height;
29402             var vw = this.viewSize[0], vh = this.viewSize[1];
29403             // only move it if it needs it
29404             var moved = false;
29405             // first validate right/bottom
29406             if(x + w > vw+s.left){
29407                 x = vw - w;
29408                 moved = true;
29409             }
29410             if(y + h > vh+s.top){
29411                 y = vh - h;
29412                 moved = true;
29413             }
29414             // then make sure top/left isn't negative
29415             if(x < s.left){
29416                 x = s.left;
29417                 moved = true;
29418             }
29419             if(y < s.top){
29420                 y = s.top;
29421                 moved = true;
29422             }
29423             if(moved){
29424                 // cache xy
29425                 this.xy = [x, y];
29426                 if(this.isVisible()){
29427                     this.el.setLocation(x, y);
29428                     this.adjustAssets();
29429                 }
29430             }
29431         }
29432     },
29433
29434     // private
29435     onDrag : function(){
29436         if(!this.proxyDrag){
29437             this.xy = this.el.getXY();
29438             this.adjustAssets();
29439         }
29440     },
29441
29442     // private
29443     adjustAssets : function(doShow){
29444         var x = this.xy[0], y = this.xy[1];
29445         var w = this.size.width, h = this.size.height;
29446         if(doShow === true){
29447             if(this.shadow){
29448                 this.shadow.show(this.el);
29449             }
29450             if(this.shim){
29451                 this.shim.show();
29452             }
29453         }
29454         if(this.shadow && this.shadow.isVisible()){
29455             this.shadow.show(this.el);
29456         }
29457         if(this.shim && this.shim.isVisible()){
29458             this.shim.setBounds(x, y, w, h);
29459         }
29460     },
29461
29462     // private
29463     adjustViewport : function(w, h){
29464         if(!w || !h){
29465             w = Roo.lib.Dom.getViewWidth();
29466             h = Roo.lib.Dom.getViewHeight();
29467         }
29468         // cache the size
29469         this.viewSize = [w, h];
29470         if(this.modal && this.mask.isVisible()){
29471             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29472             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29473         }
29474         if(this.isVisible()){
29475             this.constrainXY();
29476         }
29477     },
29478
29479     /**
29480      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29481      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29482      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29483      */
29484     destroy : function(removeEl){
29485         if(this.isVisible()){
29486             this.animateTarget = null;
29487             this.hide();
29488         }
29489         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29490         if(this.tabs){
29491             this.tabs.destroy(removeEl);
29492         }
29493         Roo.destroy(
29494              this.shim,
29495              this.proxy,
29496              this.resizer,
29497              this.close,
29498              this.mask
29499         );
29500         if(this.dd){
29501             this.dd.unreg();
29502         }
29503         if(this.buttons){
29504            for(var i = 0, len = this.buttons.length; i < len; i++){
29505                this.buttons[i].destroy();
29506            }
29507         }
29508         this.el.removeAllListeners();
29509         if(removeEl === true){
29510             this.el.update("");
29511             this.el.remove();
29512         }
29513         Roo.DialogManager.unregister(this);
29514     },
29515
29516     // private
29517     startMove : function(){
29518         if(this.proxyDrag){
29519             this.proxy.show();
29520         }
29521         if(this.constraintoviewport !== false){
29522             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29523         }
29524     },
29525
29526     // private
29527     endMove : function(){
29528         if(!this.proxyDrag){
29529             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29530         }else{
29531             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29532             this.proxy.hide();
29533         }
29534         this.refreshSize();
29535         this.adjustAssets();
29536         this.focus();
29537         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29538     },
29539
29540     /**
29541      * Brings this dialog to the front of any other visible dialogs
29542      * @return {Roo.BasicDialog} this
29543      */
29544     toFront : function(){
29545         Roo.DialogManager.bringToFront(this);
29546         return this;
29547     },
29548
29549     /**
29550      * Sends this dialog to the back (under) of any other visible dialogs
29551      * @return {Roo.BasicDialog} this
29552      */
29553     toBack : function(){
29554         Roo.DialogManager.sendToBack(this);
29555         return this;
29556     },
29557
29558     /**
29559      * Centers this dialog in the viewport
29560      * @return {Roo.BasicDialog} this
29561      */
29562     center : function(){
29563         var xy = this.el.getCenterXY(true);
29564         this.moveTo(xy[0], xy[1]);
29565         return this;
29566     },
29567
29568     /**
29569      * Moves the dialog's top-left corner to the specified point
29570      * @param {Number} x
29571      * @param {Number} y
29572      * @return {Roo.BasicDialog} this
29573      */
29574     moveTo : function(x, y){
29575         this.xy = [x,y];
29576         if(this.isVisible()){
29577             this.el.setXY(this.xy);
29578             this.adjustAssets();
29579         }
29580         return this;
29581     },
29582
29583     /**
29584      * Aligns the dialog to the specified element
29585      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29586      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29587      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29588      * @return {Roo.BasicDialog} this
29589      */
29590     alignTo : function(element, position, offsets){
29591         this.xy = this.el.getAlignToXY(element, position, offsets);
29592         if(this.isVisible()){
29593             this.el.setXY(this.xy);
29594             this.adjustAssets();
29595         }
29596         return this;
29597     },
29598
29599     /**
29600      * Anchors an element to another element and realigns it when the window is resized.
29601      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29602      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29603      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29604      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29605      * is a number, it is used as the buffer delay (defaults to 50ms).
29606      * @return {Roo.BasicDialog} this
29607      */
29608     anchorTo : function(el, alignment, offsets, monitorScroll){
29609         var action = function(){
29610             this.alignTo(el, alignment, offsets);
29611         };
29612         Roo.EventManager.onWindowResize(action, this);
29613         var tm = typeof monitorScroll;
29614         if(tm != 'undefined'){
29615             Roo.EventManager.on(window, 'scroll', action, this,
29616                 {buffer: tm == 'number' ? monitorScroll : 50});
29617         }
29618         action.call(this);
29619         return this;
29620     },
29621
29622     /**
29623      * Returns true if the dialog is visible
29624      * @return {Boolean}
29625      */
29626     isVisible : function(){
29627         return this.el.isVisible();
29628     },
29629
29630     // private
29631     animHide : function(callback){
29632         var b = Roo.get(this.animateTarget).getBox();
29633         this.proxy.show();
29634         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29635         this.el.hide();
29636         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29637                     this.hideEl.createDelegate(this, [callback]));
29638     },
29639
29640     /**
29641      * Hides the dialog.
29642      * @param {Function} callback (optional) Function to call when the dialog is hidden
29643      * @return {Roo.BasicDialog} this
29644      */
29645     hide : function(callback){
29646         if (this.fireEvent("beforehide", this) === false){
29647             return;
29648         }
29649         if(this.shadow){
29650             this.shadow.hide();
29651         }
29652         if(this.shim) {
29653           this.shim.hide();
29654         }
29655         // sometimes animateTarget seems to get set.. causing problems...
29656         // this just double checks..
29657         if(this.animateTarget && Roo.get(this.animateTarget)) {
29658            this.animHide(callback);
29659         }else{
29660             this.el.hide();
29661             this.hideEl(callback);
29662         }
29663         return this;
29664     },
29665
29666     // private
29667     hideEl : function(callback){
29668         this.proxy.hide();
29669         if(this.modal){
29670             this.mask.hide();
29671             Roo.get(document.body).removeClass("x-body-masked");
29672         }
29673         this.fireEvent("hide", this);
29674         if(typeof callback == "function"){
29675             callback();
29676         }
29677     },
29678
29679     // private
29680     hideAction : function(){
29681         this.setLeft("-10000px");
29682         this.setTop("-10000px");
29683         this.setStyle("visibility", "hidden");
29684     },
29685
29686     // private
29687     refreshSize : function(){
29688         this.size = this.el.getSize();
29689         this.xy = this.el.getXY();
29690         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29691     },
29692
29693     // private
29694     // z-index is managed by the DialogManager and may be overwritten at any time
29695     setZIndex : function(index){
29696         if(this.modal){
29697             this.mask.setStyle("z-index", index);
29698         }
29699         if(this.shim){
29700             this.shim.setStyle("z-index", ++index);
29701         }
29702         if(this.shadow){
29703             this.shadow.setZIndex(++index);
29704         }
29705         this.el.setStyle("z-index", ++index);
29706         if(this.proxy){
29707             this.proxy.setStyle("z-index", ++index);
29708         }
29709         if(this.resizer){
29710             this.resizer.proxy.setStyle("z-index", ++index);
29711         }
29712
29713         this.lastZIndex = index;
29714     },
29715
29716     /**
29717      * Returns the element for this dialog
29718      * @return {Roo.Element} The underlying dialog Element
29719      */
29720     getEl : function(){
29721         return this.el;
29722     }
29723 });
29724
29725 /**
29726  * @class Roo.DialogManager
29727  * Provides global access to BasicDialogs that have been created and
29728  * support for z-indexing (layering) multiple open dialogs.
29729  */
29730 Roo.DialogManager = function(){
29731     var list = {};
29732     var accessList = [];
29733     var front = null;
29734
29735     // private
29736     var sortDialogs = function(d1, d2){
29737         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29738     };
29739
29740     // private
29741     var orderDialogs = function(){
29742         accessList.sort(sortDialogs);
29743         var seed = Roo.DialogManager.zseed;
29744         for(var i = 0, len = accessList.length; i < len; i++){
29745             var dlg = accessList[i];
29746             if(dlg){
29747                 dlg.setZIndex(seed + (i*10));
29748             }
29749         }
29750     };
29751
29752     return {
29753         /**
29754          * The starting z-index for BasicDialogs (defaults to 9000)
29755          * @type Number The z-index value
29756          */
29757         zseed : 9000,
29758
29759         // private
29760         register : function(dlg){
29761             list[dlg.id] = dlg;
29762             accessList.push(dlg);
29763         },
29764
29765         // private
29766         unregister : function(dlg){
29767             delete list[dlg.id];
29768             var i=0;
29769             var len=0;
29770             if(!accessList.indexOf){
29771                 for(  i = 0, len = accessList.length; i < len; i++){
29772                     if(accessList[i] == dlg){
29773                         accessList.splice(i, 1);
29774                         return;
29775                     }
29776                 }
29777             }else{
29778                  i = accessList.indexOf(dlg);
29779                 if(i != -1){
29780                     accessList.splice(i, 1);
29781                 }
29782             }
29783         },
29784
29785         /**
29786          * Gets a registered dialog by id
29787          * @param {String/Object} id The id of the dialog or a dialog
29788          * @return {Roo.BasicDialog} this
29789          */
29790         get : function(id){
29791             return typeof id == "object" ? id : list[id];
29792         },
29793
29794         /**
29795          * Brings the specified dialog to the front
29796          * @param {String/Object} dlg The id of the dialog or a dialog
29797          * @return {Roo.BasicDialog} this
29798          */
29799         bringToFront : function(dlg){
29800             dlg = this.get(dlg);
29801             if(dlg != front){
29802                 front = dlg;
29803                 dlg._lastAccess = new Date().getTime();
29804                 orderDialogs();
29805             }
29806             return dlg;
29807         },
29808
29809         /**
29810          * Sends the specified dialog to the back
29811          * @param {String/Object} dlg The id of the dialog or a dialog
29812          * @return {Roo.BasicDialog} this
29813          */
29814         sendToBack : function(dlg){
29815             dlg = this.get(dlg);
29816             dlg._lastAccess = -(new Date().getTime());
29817             orderDialogs();
29818             return dlg;
29819         },
29820
29821         /**
29822          * Hides all dialogs
29823          */
29824         hideAll : function(){
29825             for(var id in list){
29826                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29827                     list[id].hide();
29828                 }
29829             }
29830         }
29831     };
29832 }();
29833
29834 /**
29835  * @class Roo.LayoutDialog
29836  * @extends Roo.BasicDialog
29837  * Dialog which provides adjustments for working with a layout in a Dialog.
29838  * Add your necessary layout config options to the dialog's config.<br>
29839  * Example usage (including a nested layout):
29840  * <pre><code>
29841 if(!dialog){
29842     dialog = new Roo.LayoutDialog("download-dlg", {
29843         modal: true,
29844         width:600,
29845         height:450,
29846         shadow:true,
29847         minWidth:500,
29848         minHeight:350,
29849         autoTabs:true,
29850         proxyDrag:true,
29851         // layout config merges with the dialog config
29852         center:{
29853             tabPosition: "top",
29854             alwaysShowTabs: true
29855         }
29856     });
29857     dialog.addKeyListener(27, dialog.hide, dialog);
29858     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29859     dialog.addButton("Build It!", this.getDownload, this);
29860
29861     // we can even add nested layouts
29862     var innerLayout = new Roo.BorderLayout("dl-inner", {
29863         east: {
29864             initialSize: 200,
29865             autoScroll:true,
29866             split:true
29867         },
29868         center: {
29869             autoScroll:true
29870         }
29871     });
29872     innerLayout.beginUpdate();
29873     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29874     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29875     innerLayout.endUpdate(true);
29876
29877     var layout = dialog.getLayout();
29878     layout.beginUpdate();
29879     layout.add("center", new Roo.ContentPanel("standard-panel",
29880                         {title: "Download the Source", fitToFrame:true}));
29881     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29882                {title: "Build your own roo.js"}));
29883     layout.getRegion("center").showPanel(sp);
29884     layout.endUpdate();
29885 }
29886 </code></pre>
29887     * @constructor
29888     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29889     * @param {Object} config configuration options
29890   */
29891 Roo.LayoutDialog = function(el, cfg){
29892     
29893     var config=  cfg;
29894     if (typeof(cfg) == 'undefined') {
29895         config = Roo.apply({}, el);
29896         // not sure why we use documentElement here.. - it should always be body.
29897         // IE7 borks horribly if we use documentElement.
29898         // webkit also does not like documentElement - it creates a body element...
29899         el = Roo.get( document.body || document.documentElement ).createChild();
29900         //config.autoCreate = true;
29901     }
29902     
29903     
29904     config.autoTabs = false;
29905     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29906     this.body.setStyle({overflow:"hidden", position:"relative"});
29907     this.layout = new Roo.BorderLayout(this.body.dom, config);
29908     this.layout.monitorWindowResize = false;
29909     this.el.addClass("x-dlg-auto-layout");
29910     // fix case when center region overwrites center function
29911     this.center = Roo.BasicDialog.prototype.center;
29912     this.on("show", this.layout.layout, this.layout, true);
29913     if (config.items) {
29914         var xitems = config.items;
29915         delete config.items;
29916         Roo.each(xitems, this.addxtype, this);
29917     }
29918     
29919     
29920 };
29921 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29922     /**
29923      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29924      * @deprecated
29925      */
29926     endUpdate : function(){
29927         this.layout.endUpdate();
29928     },
29929
29930     /**
29931      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29932      *  @deprecated
29933      */
29934     beginUpdate : function(){
29935         this.layout.beginUpdate();
29936     },
29937
29938     /**
29939      * Get the BorderLayout for this dialog
29940      * @return {Roo.BorderLayout}
29941      */
29942     getLayout : function(){
29943         return this.layout;
29944     },
29945
29946     showEl : function(){
29947         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29948         if(Roo.isIE7){
29949             this.layout.layout();
29950         }
29951     },
29952
29953     // private
29954     // Use the syncHeightBeforeShow config option to control this automatically
29955     syncBodyHeight : function(){
29956         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29957         if(this.layout){this.layout.layout();}
29958     },
29959     
29960       /**
29961      * Add an xtype element (actually adds to the layout.)
29962      * @return {Object} xdata xtype object data.
29963      */
29964     
29965     addxtype : function(c) {
29966         return this.layout.addxtype(c);
29967     }
29968 });/*
29969  * Based on:
29970  * Ext JS Library 1.1.1
29971  * Copyright(c) 2006-2007, Ext JS, LLC.
29972  *
29973  * Originally Released Under LGPL - original licence link has changed is not relivant.
29974  *
29975  * Fork - LGPL
29976  * <script type="text/javascript">
29977  */
29978  
29979 /**
29980  * @class Roo.MessageBox
29981  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29982  * Example usage:
29983  *<pre><code>
29984 // Basic alert:
29985 Roo.Msg.alert('Status', 'Changes saved successfully.');
29986
29987 // Prompt for user data:
29988 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29989     if (btn == 'ok'){
29990         // process text value...
29991     }
29992 });
29993
29994 // Show a dialog using config options:
29995 Roo.Msg.show({
29996    title:'Save Changes?',
29997    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29998    buttons: Roo.Msg.YESNOCANCEL,
29999    fn: processResult,
30000    animEl: 'elId'
30001 });
30002 </code></pre>
30003  * @singleton
30004  */
30005 Roo.MessageBox = function(){
30006     var dlg, opt, mask, waitTimer;
30007     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30008     var buttons, activeTextEl, bwidth;
30009
30010     // private
30011     var handleButton = function(button){
30012         dlg.hide();
30013         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30014     };
30015
30016     // private
30017     var handleHide = function(){
30018         if(opt && opt.cls){
30019             dlg.el.removeClass(opt.cls);
30020         }
30021         if(waitTimer){
30022             Roo.TaskMgr.stop(waitTimer);
30023             waitTimer = null;
30024         }
30025     };
30026
30027     // private
30028     var updateButtons = function(b){
30029         var width = 0;
30030         if(!b){
30031             buttons["ok"].hide();
30032             buttons["cancel"].hide();
30033             buttons["yes"].hide();
30034             buttons["no"].hide();
30035             dlg.footer.dom.style.display = 'none';
30036             return width;
30037         }
30038         dlg.footer.dom.style.display = '';
30039         for(var k in buttons){
30040             if(typeof buttons[k] != "function"){
30041                 if(b[k]){
30042                     buttons[k].show();
30043                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30044                     width += buttons[k].el.getWidth()+15;
30045                 }else{
30046                     buttons[k].hide();
30047                 }
30048             }
30049         }
30050         return width;
30051     };
30052
30053     // private
30054     var handleEsc = function(d, k, e){
30055         if(opt && opt.closable !== false){
30056             dlg.hide();
30057         }
30058         if(e){
30059             e.stopEvent();
30060         }
30061     };
30062
30063     return {
30064         /**
30065          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30066          * @return {Roo.BasicDialog} The BasicDialog element
30067          */
30068         getDialog : function(){
30069            if(!dlg){
30070                 dlg = new Roo.BasicDialog("x-msg-box", {
30071                     autoCreate : true,
30072                     shadow: true,
30073                     draggable: true,
30074                     resizable:false,
30075                     constraintoviewport:false,
30076                     fixedcenter:true,
30077                     collapsible : false,
30078                     shim:true,
30079                     modal: true,
30080                     width:400, height:100,
30081                     buttonAlign:"center",
30082                     closeClick : function(){
30083                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30084                             handleButton("no");
30085                         }else{
30086                             handleButton("cancel");
30087                         }
30088                     }
30089                 });
30090                 dlg.on("hide", handleHide);
30091                 mask = dlg.mask;
30092                 dlg.addKeyListener(27, handleEsc);
30093                 buttons = {};
30094                 var bt = this.buttonText;
30095                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30096                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30097                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30098                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30099                 bodyEl = dlg.body.createChild({
30100
30101                     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>'
30102                 });
30103                 msgEl = bodyEl.dom.firstChild;
30104                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30105                 textboxEl.enableDisplayMode();
30106                 textboxEl.addKeyListener([10,13], function(){
30107                     if(dlg.isVisible() && opt && opt.buttons){
30108                         if(opt.buttons.ok){
30109                             handleButton("ok");
30110                         }else if(opt.buttons.yes){
30111                             handleButton("yes");
30112                         }
30113                     }
30114                 });
30115                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30116                 textareaEl.enableDisplayMode();
30117                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30118                 progressEl.enableDisplayMode();
30119                 var pf = progressEl.dom.firstChild;
30120                 if (pf) {
30121                     pp = Roo.get(pf.firstChild);
30122                     pp.setHeight(pf.offsetHeight);
30123                 }
30124                 
30125             }
30126             return dlg;
30127         },
30128
30129         /**
30130          * Updates the message box body text
30131          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30132          * the XHTML-compliant non-breaking space character '&amp;#160;')
30133          * @return {Roo.MessageBox} This message box
30134          */
30135         updateText : function(text){
30136             if(!dlg.isVisible() && !opt.width){
30137                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30138             }
30139             msgEl.innerHTML = text || '&#160;';
30140             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30141                         Math.max(opt.minWidth || this.minWidth, bwidth));
30142             if(opt.prompt){
30143                 activeTextEl.setWidth(w);
30144             }
30145             if(dlg.isVisible()){
30146                 dlg.fixedcenter = false;
30147             }
30148             dlg.setContentSize(w, bodyEl.getHeight());
30149             if(dlg.isVisible()){
30150                 dlg.fixedcenter = true;
30151             }
30152             return this;
30153         },
30154
30155         /**
30156          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30157          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30158          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30159          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30160          * @return {Roo.MessageBox} This message box
30161          */
30162         updateProgress : function(value, text){
30163             if(text){
30164                 this.updateText(text);
30165             }
30166             if (pp) { // weird bug on my firefox - for some reason this is not defined
30167                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30168             }
30169             return this;
30170         },        
30171
30172         /**
30173          * Returns true if the message box is currently displayed
30174          * @return {Boolean} True if the message box is visible, else false
30175          */
30176         isVisible : function(){
30177             return dlg && dlg.isVisible();  
30178         },
30179
30180         /**
30181          * Hides the message box if it is displayed
30182          */
30183         hide : function(){
30184             if(this.isVisible()){
30185                 dlg.hide();
30186             }  
30187         },
30188
30189         /**
30190          * Displays a new message box, or reinitializes an existing message box, based on the config options
30191          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30192          * The following config object properties are supported:
30193          * <pre>
30194 Property    Type             Description
30195 ----------  ---------------  ------------------------------------------------------------------------------------
30196 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30197                                    closes (defaults to undefined)
30198 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30199                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30200 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30201                                    progress and wait dialogs will ignore this property and always hide the
30202                                    close button as they can only be closed programmatically.
30203 cls               String           A custom CSS class to apply to the message box element
30204 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30205                                    displayed (defaults to 75)
30206 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30207                                    function will be btn (the name of the button that was clicked, if applicable,
30208                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30209                                    Progress and wait dialogs will ignore this option since they do not respond to
30210                                    user actions and can only be closed programmatically, so any required function
30211                                    should be called by the same code after it closes the dialog.
30212 icon              String           A CSS class that provides a background image to be used as an icon for
30213                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30214 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30215 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30216 modal             Boolean          False to allow user interaction with the page while the message box is
30217                                    displayed (defaults to true)
30218 msg               String           A string that will replace the existing message box body text (defaults
30219                                    to the XHTML-compliant non-breaking space character '&#160;')
30220 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30221 progress          Boolean          True to display a progress bar (defaults to false)
30222 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30223 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30224 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30225 title             String           The title text
30226 value             String           The string value to set into the active textbox element if displayed
30227 wait              Boolean          True to display a progress bar (defaults to false)
30228 width             Number           The width of the dialog in pixels
30229 </pre>
30230          *
30231          * Example usage:
30232          * <pre><code>
30233 Roo.Msg.show({
30234    title: 'Address',
30235    msg: 'Please enter your address:',
30236    width: 300,
30237    buttons: Roo.MessageBox.OKCANCEL,
30238    multiline: true,
30239    fn: saveAddress,
30240    animEl: 'addAddressBtn'
30241 });
30242 </code></pre>
30243          * @param {Object} config Configuration options
30244          * @return {Roo.MessageBox} This message box
30245          */
30246         show : function(options)
30247         {
30248             
30249             // this causes nightmares if you show one dialog after another
30250             // especially on callbacks..
30251              
30252             if(this.isVisible()){
30253                 
30254                 this.hide();
30255                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30256                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30257                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30258                 
30259             }
30260             var d = this.getDialog();
30261             opt = options;
30262             d.setTitle(opt.title || "&#160;");
30263             d.close.setDisplayed(opt.closable !== false);
30264             activeTextEl = textboxEl;
30265             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30266             if(opt.prompt){
30267                 if(opt.multiline){
30268                     textboxEl.hide();
30269                     textareaEl.show();
30270                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30271                         opt.multiline : this.defaultTextHeight);
30272                     activeTextEl = textareaEl;
30273                 }else{
30274                     textboxEl.show();
30275                     textareaEl.hide();
30276                 }
30277             }else{
30278                 textboxEl.hide();
30279                 textareaEl.hide();
30280             }
30281             progressEl.setDisplayed(opt.progress === true);
30282             this.updateProgress(0);
30283             activeTextEl.dom.value = opt.value || "";
30284             if(opt.prompt){
30285                 dlg.setDefaultButton(activeTextEl);
30286             }else{
30287                 var bs = opt.buttons;
30288                 var db = null;
30289                 if(bs && bs.ok){
30290                     db = buttons["ok"];
30291                 }else if(bs && bs.yes){
30292                     db = buttons["yes"];
30293                 }
30294                 dlg.setDefaultButton(db);
30295             }
30296             bwidth = updateButtons(opt.buttons);
30297             this.updateText(opt.msg);
30298             if(opt.cls){
30299                 d.el.addClass(opt.cls);
30300             }
30301             d.proxyDrag = opt.proxyDrag === true;
30302             d.modal = opt.modal !== false;
30303             d.mask = opt.modal !== false ? mask : false;
30304             if(!d.isVisible()){
30305                 // force it to the end of the z-index stack so it gets a cursor in FF
30306                 document.body.appendChild(dlg.el.dom);
30307                 d.animateTarget = null;
30308                 d.show(options.animEl);
30309             }
30310             return this;
30311         },
30312
30313         /**
30314          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30315          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30316          * and closing the message box when the process is complete.
30317          * @param {String} title The title bar text
30318          * @param {String} msg The message box body text
30319          * @return {Roo.MessageBox} This message box
30320          */
30321         progress : function(title, msg){
30322             this.show({
30323                 title : title,
30324                 msg : msg,
30325                 buttons: false,
30326                 progress:true,
30327                 closable:false,
30328                 minWidth: this.minProgressWidth,
30329                 modal : true
30330             });
30331             return this;
30332         },
30333
30334         /**
30335          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30336          * If a callback function is passed it will be called after the user clicks the button, and the
30337          * id of the button that was clicked will be passed as the only parameter to the callback
30338          * (could also be the top-right close button).
30339          * @param {String} title The title bar text
30340          * @param {String} msg The message box body text
30341          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30342          * @param {Object} scope (optional) The scope of the callback function
30343          * @return {Roo.MessageBox} This message box
30344          */
30345         alert : function(title, msg, fn, scope){
30346             this.show({
30347                 title : title,
30348                 msg : msg,
30349                 buttons: this.OK,
30350                 fn: fn,
30351                 scope : scope,
30352                 modal : true
30353             });
30354             return this;
30355         },
30356
30357         /**
30358          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30359          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30360          * You are responsible for closing the message box when the process is complete.
30361          * @param {String} msg The message box body text
30362          * @param {String} title (optional) The title bar text
30363          * @return {Roo.MessageBox} This message box
30364          */
30365         wait : function(msg, title){
30366             this.show({
30367                 title : title,
30368                 msg : msg,
30369                 buttons: false,
30370                 closable:false,
30371                 progress:true,
30372                 modal:true,
30373                 width:300,
30374                 wait:true
30375             });
30376             waitTimer = Roo.TaskMgr.start({
30377                 run: function(i){
30378                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30379                 },
30380                 interval: 1000
30381             });
30382             return this;
30383         },
30384
30385         /**
30386          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30387          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30388          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30389          * @param {String} title The title bar text
30390          * @param {String} msg The message box body text
30391          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30392          * @param {Object} scope (optional) The scope of the callback function
30393          * @return {Roo.MessageBox} This message box
30394          */
30395         confirm : function(title, msg, fn, scope){
30396             this.show({
30397                 title : title,
30398                 msg : msg,
30399                 buttons: this.YESNO,
30400                 fn: fn,
30401                 scope : scope,
30402                 modal : true
30403             });
30404             return this;
30405         },
30406
30407         /**
30408          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30409          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30410          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30411          * (could also be the top-right close button) and the text that was entered will be passed as the two
30412          * parameters to the callback.
30413          * @param {String} title The title bar text
30414          * @param {String} msg The message box body text
30415          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30416          * @param {Object} scope (optional) The scope of the callback function
30417          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30418          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30419          * @return {Roo.MessageBox} This message box
30420          */
30421         prompt : function(title, msg, fn, scope, multiline){
30422             this.show({
30423                 title : title,
30424                 msg : msg,
30425                 buttons: this.OKCANCEL,
30426                 fn: fn,
30427                 minWidth:250,
30428                 scope : scope,
30429                 prompt:true,
30430                 multiline: multiline,
30431                 modal : true
30432             });
30433             return this;
30434         },
30435
30436         /**
30437          * Button config that displays a single OK button
30438          * @type Object
30439          */
30440         OK : {ok:true},
30441         /**
30442          * Button config that displays Yes and No buttons
30443          * @type Object
30444          */
30445         YESNO : {yes:true, no:true},
30446         /**
30447          * Button config that displays OK and Cancel buttons
30448          * @type Object
30449          */
30450         OKCANCEL : {ok:true, cancel:true},
30451         /**
30452          * Button config that displays Yes, No and Cancel buttons
30453          * @type Object
30454          */
30455         YESNOCANCEL : {yes:true, no:true, cancel:true},
30456
30457         /**
30458          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30459          * @type Number
30460          */
30461         defaultTextHeight : 75,
30462         /**
30463          * The maximum width in pixels of the message box (defaults to 600)
30464          * @type Number
30465          */
30466         maxWidth : 600,
30467         /**
30468          * The minimum width in pixels of the message box (defaults to 100)
30469          * @type Number
30470          */
30471         minWidth : 100,
30472         /**
30473          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30474          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30475          * @type Number
30476          */
30477         minProgressWidth : 250,
30478         /**
30479          * An object containing the default button text strings that can be overriden for localized language support.
30480          * Supported properties are: ok, cancel, yes and no.
30481          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30482          * @type Object
30483          */
30484         buttonText : {
30485             ok : "OK",
30486             cancel : "Cancel",
30487             yes : "Yes",
30488             no : "No"
30489         }
30490     };
30491 }();
30492
30493 /**
30494  * Shorthand for {@link Roo.MessageBox}
30495  */
30496 Roo.Msg = Roo.MessageBox;/*
30497  * Based on:
30498  * Ext JS Library 1.1.1
30499  * Copyright(c) 2006-2007, Ext JS, LLC.
30500  *
30501  * Originally Released Under LGPL - original licence link has changed is not relivant.
30502  *
30503  * Fork - LGPL
30504  * <script type="text/javascript">
30505  */
30506 /**
30507  * @class Roo.QuickTips
30508  * Provides attractive and customizable tooltips for any element.
30509  * @singleton
30510  */
30511 Roo.QuickTips = function(){
30512     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30513     var ce, bd, xy, dd;
30514     var visible = false, disabled = true, inited = false;
30515     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30516     
30517     var onOver = function(e){
30518         if(disabled){
30519             return;
30520         }
30521         var t = e.getTarget();
30522         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30523             return;
30524         }
30525         if(ce && t == ce.el){
30526             clearTimeout(hideProc);
30527             return;
30528         }
30529         if(t && tagEls[t.id]){
30530             tagEls[t.id].el = t;
30531             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30532             return;
30533         }
30534         var ttp, et = Roo.fly(t);
30535         var ns = cfg.namespace;
30536         if(tm.interceptTitles && t.title){
30537             ttp = t.title;
30538             t.qtip = ttp;
30539             t.removeAttribute("title");
30540             e.preventDefault();
30541         }else{
30542             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30543         }
30544         if(ttp){
30545             showProc = show.defer(tm.showDelay, tm, [{
30546                 el: t, 
30547                 text: ttp, 
30548                 width: et.getAttributeNS(ns, cfg.width),
30549                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30550                 title: et.getAttributeNS(ns, cfg.title),
30551                     cls: et.getAttributeNS(ns, cfg.cls)
30552             }]);
30553         }
30554     };
30555     
30556     var onOut = function(e){
30557         clearTimeout(showProc);
30558         var t = e.getTarget();
30559         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30560             hideProc = setTimeout(hide, tm.hideDelay);
30561         }
30562     };
30563     
30564     var onMove = function(e){
30565         if(disabled){
30566             return;
30567         }
30568         xy = e.getXY();
30569         xy[1] += 18;
30570         if(tm.trackMouse && ce){
30571             el.setXY(xy);
30572         }
30573     };
30574     
30575     var onDown = function(e){
30576         clearTimeout(showProc);
30577         clearTimeout(hideProc);
30578         if(!e.within(el)){
30579             if(tm.hideOnClick){
30580                 hide();
30581                 tm.disable();
30582                 tm.enable.defer(100, tm);
30583             }
30584         }
30585     };
30586     
30587     var getPad = function(){
30588         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30589     };
30590
30591     var show = function(o){
30592         if(disabled){
30593             return;
30594         }
30595         clearTimeout(dismissProc);
30596         ce = o;
30597         if(removeCls){ // in case manually hidden
30598             el.removeClass(removeCls);
30599             removeCls = null;
30600         }
30601         if(ce.cls){
30602             el.addClass(ce.cls);
30603             removeCls = ce.cls;
30604         }
30605         if(ce.title){
30606             tipTitle.update(ce.title);
30607             tipTitle.show();
30608         }else{
30609             tipTitle.update('');
30610             tipTitle.hide();
30611         }
30612         el.dom.style.width  = tm.maxWidth+'px';
30613         //tipBody.dom.style.width = '';
30614         tipBodyText.update(o.text);
30615         var p = getPad(), w = ce.width;
30616         if(!w){
30617             var td = tipBodyText.dom;
30618             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30619             if(aw > tm.maxWidth){
30620                 w = tm.maxWidth;
30621             }else if(aw < tm.minWidth){
30622                 w = tm.minWidth;
30623             }else{
30624                 w = aw;
30625             }
30626         }
30627         //tipBody.setWidth(w);
30628         el.setWidth(parseInt(w, 10) + p);
30629         if(ce.autoHide === false){
30630             close.setDisplayed(true);
30631             if(dd){
30632                 dd.unlock();
30633             }
30634         }else{
30635             close.setDisplayed(false);
30636             if(dd){
30637                 dd.lock();
30638             }
30639         }
30640         if(xy){
30641             el.avoidY = xy[1]-18;
30642             el.setXY(xy);
30643         }
30644         if(tm.animate){
30645             el.setOpacity(.1);
30646             el.setStyle("visibility", "visible");
30647             el.fadeIn({callback: afterShow});
30648         }else{
30649             afterShow();
30650         }
30651     };
30652     
30653     var afterShow = function(){
30654         if(ce){
30655             el.show();
30656             esc.enable();
30657             if(tm.autoDismiss && ce.autoHide !== false){
30658                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30659             }
30660         }
30661     };
30662     
30663     var hide = function(noanim){
30664         clearTimeout(dismissProc);
30665         clearTimeout(hideProc);
30666         ce = null;
30667         if(el.isVisible()){
30668             esc.disable();
30669             if(noanim !== true && tm.animate){
30670                 el.fadeOut({callback: afterHide});
30671             }else{
30672                 afterHide();
30673             } 
30674         }
30675     };
30676     
30677     var afterHide = function(){
30678         el.hide();
30679         if(removeCls){
30680             el.removeClass(removeCls);
30681             removeCls = null;
30682         }
30683     };
30684     
30685     return {
30686         /**
30687         * @cfg {Number} minWidth
30688         * The minimum width of the quick tip (defaults to 40)
30689         */
30690        minWidth : 40,
30691         /**
30692         * @cfg {Number} maxWidth
30693         * The maximum width of the quick tip (defaults to 300)
30694         */
30695        maxWidth : 300,
30696         /**
30697         * @cfg {Boolean} interceptTitles
30698         * True to automatically use the element's DOM title value if available (defaults to false)
30699         */
30700        interceptTitles : false,
30701         /**
30702         * @cfg {Boolean} trackMouse
30703         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30704         */
30705        trackMouse : false,
30706         /**
30707         * @cfg {Boolean} hideOnClick
30708         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30709         */
30710        hideOnClick : true,
30711         /**
30712         * @cfg {Number} showDelay
30713         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30714         */
30715        showDelay : 500,
30716         /**
30717         * @cfg {Number} hideDelay
30718         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30719         */
30720        hideDelay : 200,
30721         /**
30722         * @cfg {Boolean} autoHide
30723         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30724         * Used in conjunction with hideDelay.
30725         */
30726        autoHide : true,
30727         /**
30728         * @cfg {Boolean}
30729         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30730         * (defaults to true).  Used in conjunction with autoDismissDelay.
30731         */
30732        autoDismiss : true,
30733         /**
30734         * @cfg {Number}
30735         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30736         */
30737        autoDismissDelay : 5000,
30738        /**
30739         * @cfg {Boolean} animate
30740         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30741         */
30742        animate : false,
30743
30744        /**
30745         * @cfg {String} title
30746         * Title text to display (defaults to '').  This can be any valid HTML markup.
30747         */
30748         title: '',
30749        /**
30750         * @cfg {String} text
30751         * Body text to display (defaults to '').  This can be any valid HTML markup.
30752         */
30753         text : '',
30754        /**
30755         * @cfg {String} cls
30756         * A CSS class to apply to the base quick tip element (defaults to '').
30757         */
30758         cls : '',
30759        /**
30760         * @cfg {Number} width
30761         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30762         * minWidth or maxWidth.
30763         */
30764         width : null,
30765
30766     /**
30767      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30768      * or display QuickTips in a page.
30769      */
30770        init : function(){
30771           tm = Roo.QuickTips;
30772           cfg = tm.tagConfig;
30773           if(!inited){
30774               if(!Roo.isReady){ // allow calling of init() before onReady
30775                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30776                   return;
30777               }
30778               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30779               el.fxDefaults = {stopFx: true};
30780               // maximum custom styling
30781               //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>');
30782               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>');              
30783               tipTitle = el.child('h3');
30784               tipTitle.enableDisplayMode("block");
30785               tipBody = el.child('div.x-tip-bd');
30786               tipBodyText = el.child('div.x-tip-bd-inner');
30787               //bdLeft = el.child('div.x-tip-bd-left');
30788               //bdRight = el.child('div.x-tip-bd-right');
30789               close = el.child('div.x-tip-close');
30790               close.enableDisplayMode("block");
30791               close.on("click", hide);
30792               var d = Roo.get(document);
30793               d.on("mousedown", onDown);
30794               d.on("mouseover", onOver);
30795               d.on("mouseout", onOut);
30796               d.on("mousemove", onMove);
30797               esc = d.addKeyListener(27, hide);
30798               esc.disable();
30799               if(Roo.dd.DD){
30800                   dd = el.initDD("default", null, {
30801                       onDrag : function(){
30802                           el.sync();  
30803                       }
30804                   });
30805                   dd.setHandleElId(tipTitle.id);
30806                   dd.lock();
30807               }
30808               inited = true;
30809           }
30810           this.enable(); 
30811        },
30812
30813     /**
30814      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30815      * are supported:
30816      * <pre>
30817 Property    Type                   Description
30818 ----------  ---------------------  ------------------------------------------------------------------------
30819 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30820      * </ul>
30821      * @param {Object} config The config object
30822      */
30823        register : function(config){
30824            var cs = config instanceof Array ? config : arguments;
30825            for(var i = 0, len = cs.length; i < len; i++) {
30826                var c = cs[i];
30827                var target = c.target;
30828                if(target){
30829                    if(target instanceof Array){
30830                        for(var j = 0, jlen = target.length; j < jlen; j++){
30831                            tagEls[target[j]] = c;
30832                        }
30833                    }else{
30834                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30835                    }
30836                }
30837            }
30838        },
30839
30840     /**
30841      * Removes this quick tip from its element and destroys it.
30842      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30843      */
30844        unregister : function(el){
30845            delete tagEls[Roo.id(el)];
30846        },
30847
30848     /**
30849      * Enable this quick tip.
30850      */
30851        enable : function(){
30852            if(inited && disabled){
30853                locks.pop();
30854                if(locks.length < 1){
30855                    disabled = false;
30856                }
30857            }
30858        },
30859
30860     /**
30861      * Disable this quick tip.
30862      */
30863        disable : function(){
30864           disabled = true;
30865           clearTimeout(showProc);
30866           clearTimeout(hideProc);
30867           clearTimeout(dismissProc);
30868           if(ce){
30869               hide(true);
30870           }
30871           locks.push(1);
30872        },
30873
30874     /**
30875      * Returns true if the quick tip is enabled, else false.
30876      */
30877        isEnabled : function(){
30878             return !disabled;
30879        },
30880
30881         // private
30882        tagConfig : {
30883            namespace : "ext",
30884            attribute : "qtip",
30885            width : "width",
30886            target : "target",
30887            title : "qtitle",
30888            hide : "hide",
30889            cls : "qclass"
30890        }
30891    };
30892 }();
30893
30894 // backwards compat
30895 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30896  * Based on:
30897  * Ext JS Library 1.1.1
30898  * Copyright(c) 2006-2007, Ext JS, LLC.
30899  *
30900  * Originally Released Under LGPL - original licence link has changed is not relivant.
30901  *
30902  * Fork - LGPL
30903  * <script type="text/javascript">
30904  */
30905  
30906
30907 /**
30908  * @class Roo.tree.TreePanel
30909  * @extends Roo.data.Tree
30910
30911  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30912  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30913  * @cfg {Boolean} enableDD true to enable drag and drop
30914  * @cfg {Boolean} enableDrag true to enable just drag
30915  * @cfg {Boolean} enableDrop true to enable just drop
30916  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30917  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30918  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30919  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30920  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30921  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30922  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30923  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30924  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30925  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30926  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30927  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30928  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30929  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30930  * @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>
30931  * @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>
30932  * 
30933  * @constructor
30934  * @param {String/HTMLElement/Element} el The container element
30935  * @param {Object} config
30936  */
30937 Roo.tree.TreePanel = function(el, config){
30938     var root = false;
30939     var loader = false;
30940     if (config.root) {
30941         root = config.root;
30942         delete config.root;
30943     }
30944     if (config.loader) {
30945         loader = config.loader;
30946         delete config.loader;
30947     }
30948     
30949     Roo.apply(this, config);
30950     Roo.tree.TreePanel.superclass.constructor.call(this);
30951     this.el = Roo.get(el);
30952     this.el.addClass('x-tree');
30953     //console.log(root);
30954     if (root) {
30955         this.setRootNode( Roo.factory(root, Roo.tree));
30956     }
30957     if (loader) {
30958         this.loader = Roo.factory(loader, Roo.tree);
30959     }
30960    /**
30961     * Read-only. The id of the container element becomes this TreePanel's id.
30962     */
30963     this.id = this.el.id;
30964     this.addEvents({
30965         /**
30966         * @event beforeload
30967         * Fires before a node is loaded, return false to cancel
30968         * @param {Node} node The node being loaded
30969         */
30970         "beforeload" : true,
30971         /**
30972         * @event load
30973         * Fires when a node is loaded
30974         * @param {Node} node The node that was loaded
30975         */
30976         "load" : true,
30977         /**
30978         * @event textchange
30979         * Fires when the text for a node is changed
30980         * @param {Node} node The node
30981         * @param {String} text The new text
30982         * @param {String} oldText The old text
30983         */
30984         "textchange" : true,
30985         /**
30986         * @event beforeexpand
30987         * Fires before a node is expanded, return false to cancel.
30988         * @param {Node} node The node
30989         * @param {Boolean} deep
30990         * @param {Boolean} anim
30991         */
30992         "beforeexpand" : true,
30993         /**
30994         * @event beforecollapse
30995         * Fires before a node is collapsed, return false to cancel.
30996         * @param {Node} node The node
30997         * @param {Boolean} deep
30998         * @param {Boolean} anim
30999         */
31000         "beforecollapse" : true,
31001         /**
31002         * @event expand
31003         * Fires when a node is expanded
31004         * @param {Node} node The node
31005         */
31006         "expand" : true,
31007         /**
31008         * @event disabledchange
31009         * Fires when the disabled status of a node changes
31010         * @param {Node} node The node
31011         * @param {Boolean} disabled
31012         */
31013         "disabledchange" : true,
31014         /**
31015         * @event collapse
31016         * Fires when a node is collapsed
31017         * @param {Node} node The node
31018         */
31019         "collapse" : true,
31020         /**
31021         * @event beforeclick
31022         * Fires before click processing on a node. Return false to cancel the default action.
31023         * @param {Node} node The node
31024         * @param {Roo.EventObject} e The event object
31025         */
31026         "beforeclick":true,
31027         /**
31028         * @event checkchange
31029         * Fires when a node with a checkbox's checked property changes
31030         * @param {Node} this This node
31031         * @param {Boolean} checked
31032         */
31033         "checkchange":true,
31034         /**
31035         * @event click
31036         * Fires when a node is clicked
31037         * @param {Node} node The node
31038         * @param {Roo.EventObject} e The event object
31039         */
31040         "click":true,
31041         /**
31042         * @event dblclick
31043         * Fires when a node is double clicked
31044         * @param {Node} node The node
31045         * @param {Roo.EventObject} e The event object
31046         */
31047         "dblclick":true,
31048         /**
31049         * @event contextmenu
31050         * Fires when a node is right clicked
31051         * @param {Node} node The node
31052         * @param {Roo.EventObject} e The event object
31053         */
31054         "contextmenu":true,
31055         /**
31056         * @event beforechildrenrendered
31057         * Fires right before the child nodes for a node are rendered
31058         * @param {Node} node The node
31059         */
31060         "beforechildrenrendered":true,
31061         /**
31062         * @event startdrag
31063         * Fires when a node starts being dragged
31064         * @param {Roo.tree.TreePanel} this
31065         * @param {Roo.tree.TreeNode} node
31066         * @param {event} e The raw browser event
31067         */ 
31068        "startdrag" : true,
31069        /**
31070         * @event enddrag
31071         * Fires when a drag operation is complete
31072         * @param {Roo.tree.TreePanel} this
31073         * @param {Roo.tree.TreeNode} node
31074         * @param {event} e The raw browser event
31075         */
31076        "enddrag" : true,
31077        /**
31078         * @event dragdrop
31079         * Fires when a dragged node is dropped on a valid DD target
31080         * @param {Roo.tree.TreePanel} this
31081         * @param {Roo.tree.TreeNode} node
31082         * @param {DD} dd The dd it was dropped on
31083         * @param {event} e The raw browser event
31084         */
31085        "dragdrop" : true,
31086        /**
31087         * @event beforenodedrop
31088         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31089         * passed to handlers has the following properties:<br />
31090         * <ul style="padding:5px;padding-left:16px;">
31091         * <li>tree - The TreePanel</li>
31092         * <li>target - The node being targeted for the drop</li>
31093         * <li>data - The drag data from the drag source</li>
31094         * <li>point - The point of the drop - append, above or below</li>
31095         * <li>source - The drag source</li>
31096         * <li>rawEvent - Raw mouse event</li>
31097         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31098         * to be inserted by setting them on this object.</li>
31099         * <li>cancel - Set this to true to cancel the drop.</li>
31100         * </ul>
31101         * @param {Object} dropEvent
31102         */
31103        "beforenodedrop" : true,
31104        /**
31105         * @event nodedrop
31106         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31107         * passed to handlers has the following properties:<br />
31108         * <ul style="padding:5px;padding-left:16px;">
31109         * <li>tree - The TreePanel</li>
31110         * <li>target - The node being targeted for the drop</li>
31111         * <li>data - The drag data from the drag source</li>
31112         * <li>point - The point of the drop - append, above or below</li>
31113         * <li>source - The drag source</li>
31114         * <li>rawEvent - Raw mouse event</li>
31115         * <li>dropNode - Dropped node(s).</li>
31116         * </ul>
31117         * @param {Object} dropEvent
31118         */
31119        "nodedrop" : true,
31120         /**
31121         * @event nodedragover
31122         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31123         * passed to handlers has the following properties:<br />
31124         * <ul style="padding:5px;padding-left:16px;">
31125         * <li>tree - The TreePanel</li>
31126         * <li>target - The node being targeted for the drop</li>
31127         * <li>data - The drag data from the drag source</li>
31128         * <li>point - The point of the drop - append, above or below</li>
31129         * <li>source - The drag source</li>
31130         * <li>rawEvent - Raw mouse event</li>
31131         * <li>dropNode - Drop node(s) provided by the source.</li>
31132         * <li>cancel - Set this to true to signal drop not allowed.</li>
31133         * </ul>
31134         * @param {Object} dragOverEvent
31135         */
31136        "nodedragover" : true
31137         
31138     });
31139     if(this.singleExpand){
31140        this.on("beforeexpand", this.restrictExpand, this);
31141     }
31142     if (this.editor) {
31143         this.editor.tree = this;
31144         this.editor = Roo.factory(this.editor, Roo.tree);
31145     }
31146     
31147     if (this.selModel) {
31148         this.selModel = Roo.factory(this.selModel, Roo.tree);
31149     }
31150    
31151 };
31152 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31153     rootVisible : true,
31154     animate: Roo.enableFx,
31155     lines : true,
31156     enableDD : false,
31157     hlDrop : Roo.enableFx,
31158   
31159     renderer: false,
31160     
31161     rendererTip: false,
31162     // private
31163     restrictExpand : function(node){
31164         var p = node.parentNode;
31165         if(p){
31166             if(p.expandedChild && p.expandedChild.parentNode == p){
31167                 p.expandedChild.collapse();
31168             }
31169             p.expandedChild = node;
31170         }
31171     },
31172
31173     // private override
31174     setRootNode : function(node){
31175         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31176         if(!this.rootVisible){
31177             node.ui = new Roo.tree.RootTreeNodeUI(node);
31178         }
31179         return node;
31180     },
31181
31182     /**
31183      * Returns the container element for this TreePanel
31184      */
31185     getEl : function(){
31186         return this.el;
31187     },
31188
31189     /**
31190      * Returns the default TreeLoader for this TreePanel
31191      */
31192     getLoader : function(){
31193         return this.loader;
31194     },
31195
31196     /**
31197      * Expand all nodes
31198      */
31199     expandAll : function(){
31200         this.root.expand(true);
31201     },
31202
31203     /**
31204      * Collapse all nodes
31205      */
31206     collapseAll : function(){
31207         this.root.collapse(true);
31208     },
31209
31210     /**
31211      * Returns the selection model used by this TreePanel
31212      */
31213     getSelectionModel : function(){
31214         if(!this.selModel){
31215             this.selModel = new Roo.tree.DefaultSelectionModel();
31216         }
31217         return this.selModel;
31218     },
31219
31220     /**
31221      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31222      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31223      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31224      * @return {Array}
31225      */
31226     getChecked : function(a, startNode){
31227         startNode = startNode || this.root;
31228         var r = [];
31229         var f = function(){
31230             if(this.attributes.checked){
31231                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31232             }
31233         }
31234         startNode.cascade(f);
31235         return r;
31236     },
31237
31238     /**
31239      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31240      * @param {String} path
31241      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31242      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31243      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31244      */
31245     expandPath : function(path, attr, callback){
31246         attr = attr || "id";
31247         var keys = path.split(this.pathSeparator);
31248         var curNode = this.root;
31249         if(curNode.attributes[attr] != keys[1]){ // invalid root
31250             if(callback){
31251                 callback(false, null);
31252             }
31253             return;
31254         }
31255         var index = 1;
31256         var f = function(){
31257             if(++index == keys.length){
31258                 if(callback){
31259                     callback(true, curNode);
31260                 }
31261                 return;
31262             }
31263             var c = curNode.findChild(attr, keys[index]);
31264             if(!c){
31265                 if(callback){
31266                     callback(false, curNode);
31267                 }
31268                 return;
31269             }
31270             curNode = c;
31271             c.expand(false, false, f);
31272         };
31273         curNode.expand(false, false, f);
31274     },
31275
31276     /**
31277      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31278      * @param {String} path
31279      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31280      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31281      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31282      */
31283     selectPath : function(path, attr, callback){
31284         attr = attr || "id";
31285         var keys = path.split(this.pathSeparator);
31286         var v = keys.pop();
31287         if(keys.length > 0){
31288             var f = function(success, node){
31289                 if(success && node){
31290                     var n = node.findChild(attr, v);
31291                     if(n){
31292                         n.select();
31293                         if(callback){
31294                             callback(true, n);
31295                         }
31296                     }else if(callback){
31297                         callback(false, n);
31298                     }
31299                 }else{
31300                     if(callback){
31301                         callback(false, n);
31302                     }
31303                 }
31304             };
31305             this.expandPath(keys.join(this.pathSeparator), attr, f);
31306         }else{
31307             this.root.select();
31308             if(callback){
31309                 callback(true, this.root);
31310             }
31311         }
31312     },
31313
31314     getTreeEl : function(){
31315         return this.el;
31316     },
31317
31318     /**
31319      * Trigger rendering of this TreePanel
31320      */
31321     render : function(){
31322         if (this.innerCt) {
31323             return this; // stop it rendering more than once!!
31324         }
31325         
31326         this.innerCt = this.el.createChild({tag:"ul",
31327                cls:"x-tree-root-ct " +
31328                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31329
31330         if(this.containerScroll){
31331             Roo.dd.ScrollManager.register(this.el);
31332         }
31333         if((this.enableDD || this.enableDrop) && !this.dropZone){
31334            /**
31335             * The dropZone used by this tree if drop is enabled
31336             * @type Roo.tree.TreeDropZone
31337             */
31338              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31339                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31340            });
31341         }
31342         if((this.enableDD || this.enableDrag) && !this.dragZone){
31343            /**
31344             * The dragZone used by this tree if drag is enabled
31345             * @type Roo.tree.TreeDragZone
31346             */
31347             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31348                ddGroup: this.ddGroup || "TreeDD",
31349                scroll: this.ddScroll
31350            });
31351         }
31352         this.getSelectionModel().init(this);
31353         if (!this.root) {
31354             console.log("ROOT not set in tree");
31355             return;
31356         }
31357         this.root.render();
31358         if(!this.rootVisible){
31359             this.root.renderChildren();
31360         }
31361         return this;
31362     }
31363 });/*
31364  * Based on:
31365  * Ext JS Library 1.1.1
31366  * Copyright(c) 2006-2007, Ext JS, LLC.
31367  *
31368  * Originally Released Under LGPL - original licence link has changed is not relivant.
31369  *
31370  * Fork - LGPL
31371  * <script type="text/javascript">
31372  */
31373  
31374
31375 /**
31376  * @class Roo.tree.DefaultSelectionModel
31377  * @extends Roo.util.Observable
31378  * The default single selection for a TreePanel.
31379  * @param {Object} cfg Configuration
31380  */
31381 Roo.tree.DefaultSelectionModel = function(cfg){
31382    this.selNode = null;
31383    
31384    
31385    
31386    this.addEvents({
31387        /**
31388         * @event selectionchange
31389         * Fires when the selected node changes
31390         * @param {DefaultSelectionModel} this
31391         * @param {TreeNode} node the new selection
31392         */
31393        "selectionchange" : true,
31394
31395        /**
31396         * @event beforeselect
31397         * Fires before the selected node changes, return false to cancel the change
31398         * @param {DefaultSelectionModel} this
31399         * @param {TreeNode} node the new selection
31400         * @param {TreeNode} node the old selection
31401         */
31402        "beforeselect" : true
31403    });
31404    
31405     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31406 };
31407
31408 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31409     init : function(tree){
31410         this.tree = tree;
31411         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31412         tree.on("click", this.onNodeClick, this);
31413     },
31414     
31415     onNodeClick : function(node, e){
31416         if (e.ctrlKey && this.selNode == node)  {
31417             this.unselect(node);
31418             return;
31419         }
31420         this.select(node);
31421     },
31422     
31423     /**
31424      * Select a node.
31425      * @param {TreeNode} node The node to select
31426      * @return {TreeNode} The selected node
31427      */
31428     select : function(node){
31429         var last = this.selNode;
31430         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31431             if(last){
31432                 last.ui.onSelectedChange(false);
31433             }
31434             this.selNode = node;
31435             node.ui.onSelectedChange(true);
31436             this.fireEvent("selectionchange", this, node, last);
31437         }
31438         return node;
31439     },
31440     
31441     /**
31442      * Deselect a node.
31443      * @param {TreeNode} node The node to unselect
31444      */
31445     unselect : function(node){
31446         if(this.selNode == node){
31447             this.clearSelections();
31448         }    
31449     },
31450     
31451     /**
31452      * Clear all selections
31453      */
31454     clearSelections : function(){
31455         var n = this.selNode;
31456         if(n){
31457             n.ui.onSelectedChange(false);
31458             this.selNode = null;
31459             this.fireEvent("selectionchange", this, null);
31460         }
31461         return n;
31462     },
31463     
31464     /**
31465      * Get the selected node
31466      * @return {TreeNode} The selected node
31467      */
31468     getSelectedNode : function(){
31469         return this.selNode;    
31470     },
31471     
31472     /**
31473      * Returns true if the node is selected
31474      * @param {TreeNode} node The node to check
31475      * @return {Boolean}
31476      */
31477     isSelected : function(node){
31478         return this.selNode == node;  
31479     },
31480
31481     /**
31482      * Selects the node above the selected node in the tree, intelligently walking the nodes
31483      * @return TreeNode The new selection
31484      */
31485     selectPrevious : function(){
31486         var s = this.selNode || this.lastSelNode;
31487         if(!s){
31488             return null;
31489         }
31490         var ps = s.previousSibling;
31491         if(ps){
31492             if(!ps.isExpanded() || ps.childNodes.length < 1){
31493                 return this.select(ps);
31494             } else{
31495                 var lc = ps.lastChild;
31496                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31497                     lc = lc.lastChild;
31498                 }
31499                 return this.select(lc);
31500             }
31501         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31502             return this.select(s.parentNode);
31503         }
31504         return null;
31505     },
31506
31507     /**
31508      * Selects the node above the selected node in the tree, intelligently walking the nodes
31509      * @return TreeNode The new selection
31510      */
31511     selectNext : function(){
31512         var s = this.selNode || this.lastSelNode;
31513         if(!s){
31514             return null;
31515         }
31516         if(s.firstChild && s.isExpanded()){
31517              return this.select(s.firstChild);
31518          }else if(s.nextSibling){
31519              return this.select(s.nextSibling);
31520          }else if(s.parentNode){
31521             var newS = null;
31522             s.parentNode.bubble(function(){
31523                 if(this.nextSibling){
31524                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31525                     return false;
31526                 }
31527             });
31528             return newS;
31529          }
31530         return null;
31531     },
31532
31533     onKeyDown : function(e){
31534         var s = this.selNode || this.lastSelNode;
31535         // undesirable, but required
31536         var sm = this;
31537         if(!s){
31538             return;
31539         }
31540         var k = e.getKey();
31541         switch(k){
31542              case e.DOWN:
31543                  e.stopEvent();
31544                  this.selectNext();
31545              break;
31546              case e.UP:
31547                  e.stopEvent();
31548                  this.selectPrevious();
31549              break;
31550              case e.RIGHT:
31551                  e.preventDefault();
31552                  if(s.hasChildNodes()){
31553                      if(!s.isExpanded()){
31554                          s.expand();
31555                      }else if(s.firstChild){
31556                          this.select(s.firstChild, e);
31557                      }
31558                  }
31559              break;
31560              case e.LEFT:
31561                  e.preventDefault();
31562                  if(s.hasChildNodes() && s.isExpanded()){
31563                      s.collapse();
31564                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31565                      this.select(s.parentNode, e);
31566                  }
31567              break;
31568         };
31569     }
31570 });
31571
31572 /**
31573  * @class Roo.tree.MultiSelectionModel
31574  * @extends Roo.util.Observable
31575  * Multi selection for a TreePanel.
31576  * @param {Object} cfg Configuration
31577  */
31578 Roo.tree.MultiSelectionModel = function(){
31579    this.selNodes = [];
31580    this.selMap = {};
31581    this.addEvents({
31582        /**
31583         * @event selectionchange
31584         * Fires when the selected nodes change
31585         * @param {MultiSelectionModel} this
31586         * @param {Array} nodes Array of the selected nodes
31587         */
31588        "selectionchange" : true
31589    });
31590    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31591    
31592 };
31593
31594 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31595     init : function(tree){
31596         this.tree = tree;
31597         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31598         tree.on("click", this.onNodeClick, this);
31599     },
31600     
31601     onNodeClick : function(node, e){
31602         this.select(node, e, e.ctrlKey);
31603     },
31604     
31605     /**
31606      * Select a node.
31607      * @param {TreeNode} node The node to select
31608      * @param {EventObject} e (optional) An event associated with the selection
31609      * @param {Boolean} keepExisting True to retain existing selections
31610      * @return {TreeNode} The selected node
31611      */
31612     select : function(node, e, keepExisting){
31613         if(keepExisting !== true){
31614             this.clearSelections(true);
31615         }
31616         if(this.isSelected(node)){
31617             this.lastSelNode = node;
31618             return node;
31619         }
31620         this.selNodes.push(node);
31621         this.selMap[node.id] = node;
31622         this.lastSelNode = node;
31623         node.ui.onSelectedChange(true);
31624         this.fireEvent("selectionchange", this, this.selNodes);
31625         return node;
31626     },
31627     
31628     /**
31629      * Deselect a node.
31630      * @param {TreeNode} node The node to unselect
31631      */
31632     unselect : function(node){
31633         if(this.selMap[node.id]){
31634             node.ui.onSelectedChange(false);
31635             var sn = this.selNodes;
31636             var index = -1;
31637             if(sn.indexOf){
31638                 index = sn.indexOf(node);
31639             }else{
31640                 for(var i = 0, len = sn.length; i < len; i++){
31641                     if(sn[i] == node){
31642                         index = i;
31643                         break;
31644                     }
31645                 }
31646             }
31647             if(index != -1){
31648                 this.selNodes.splice(index, 1);
31649             }
31650             delete this.selMap[node.id];
31651             this.fireEvent("selectionchange", this, this.selNodes);
31652         }
31653     },
31654     
31655     /**
31656      * Clear all selections
31657      */
31658     clearSelections : function(suppressEvent){
31659         var sn = this.selNodes;
31660         if(sn.length > 0){
31661             for(var i = 0, len = sn.length; i < len; i++){
31662                 sn[i].ui.onSelectedChange(false);
31663             }
31664             this.selNodes = [];
31665             this.selMap = {};
31666             if(suppressEvent !== true){
31667                 this.fireEvent("selectionchange", this, this.selNodes);
31668             }
31669         }
31670     },
31671     
31672     /**
31673      * Returns true if the node is selected
31674      * @param {TreeNode} node The node to check
31675      * @return {Boolean}
31676      */
31677     isSelected : function(node){
31678         return this.selMap[node.id] ? true : false;  
31679     },
31680     
31681     /**
31682      * Returns an array of the selected nodes
31683      * @return {Array}
31684      */
31685     getSelectedNodes : function(){
31686         return this.selNodes;    
31687     },
31688
31689     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31690
31691     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31692
31693     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31694 });/*
31695  * Based on:
31696  * Ext JS Library 1.1.1
31697  * Copyright(c) 2006-2007, Ext JS, LLC.
31698  *
31699  * Originally Released Under LGPL - original licence link has changed is not relivant.
31700  *
31701  * Fork - LGPL
31702  * <script type="text/javascript">
31703  */
31704  
31705 /**
31706  * @class Roo.tree.TreeNode
31707  * @extends Roo.data.Node
31708  * @cfg {String} text The text for this node
31709  * @cfg {Boolean} expanded true to start the node expanded
31710  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31711  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31712  * @cfg {Boolean} disabled true to start the node disabled
31713  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31714  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31715  * @cfg {String} cls A css class to be added to the node
31716  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31717  * @cfg {String} href URL of the link used for the node (defaults to #)
31718  * @cfg {String} hrefTarget target frame for the link
31719  * @cfg {String} qtip An Ext QuickTip for the node
31720  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31721  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31722  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31723  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31724  * (defaults to undefined with no checkbox rendered)
31725  * @constructor
31726  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31727  */
31728 Roo.tree.TreeNode = function(attributes){
31729     attributes = attributes || {};
31730     if(typeof attributes == "string"){
31731         attributes = {text: attributes};
31732     }
31733     this.childrenRendered = false;
31734     this.rendered = false;
31735     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31736     this.expanded = attributes.expanded === true;
31737     this.isTarget = attributes.isTarget !== false;
31738     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31739     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31740
31741     /**
31742      * Read-only. The text for this node. To change it use setText().
31743      * @type String
31744      */
31745     this.text = attributes.text;
31746     /**
31747      * True if this node is disabled.
31748      * @type Boolean
31749      */
31750     this.disabled = attributes.disabled === true;
31751
31752     this.addEvents({
31753         /**
31754         * @event textchange
31755         * Fires when the text for this node is changed
31756         * @param {Node} this This node
31757         * @param {String} text The new text
31758         * @param {String} oldText The old text
31759         */
31760         "textchange" : true,
31761         /**
31762         * @event beforeexpand
31763         * Fires before this node is expanded, return false to cancel.
31764         * @param {Node} this This node
31765         * @param {Boolean} deep
31766         * @param {Boolean} anim
31767         */
31768         "beforeexpand" : true,
31769         /**
31770         * @event beforecollapse
31771         * Fires before this node is collapsed, return false to cancel.
31772         * @param {Node} this This node
31773         * @param {Boolean} deep
31774         * @param {Boolean} anim
31775         */
31776         "beforecollapse" : true,
31777         /**
31778         * @event expand
31779         * Fires when this node is expanded
31780         * @param {Node} this This node
31781         */
31782         "expand" : true,
31783         /**
31784         * @event disabledchange
31785         * Fires when the disabled status of this node changes
31786         * @param {Node} this This node
31787         * @param {Boolean} disabled
31788         */
31789         "disabledchange" : true,
31790         /**
31791         * @event collapse
31792         * Fires when this node is collapsed
31793         * @param {Node} this This node
31794         */
31795         "collapse" : true,
31796         /**
31797         * @event beforeclick
31798         * Fires before click processing. Return false to cancel the default action.
31799         * @param {Node} this This node
31800         * @param {Roo.EventObject} e The event object
31801         */
31802         "beforeclick":true,
31803         /**
31804         * @event checkchange
31805         * Fires when a node with a checkbox's checked property changes
31806         * @param {Node} this This node
31807         * @param {Boolean} checked
31808         */
31809         "checkchange":true,
31810         /**
31811         * @event click
31812         * Fires when this node is clicked
31813         * @param {Node} this This node
31814         * @param {Roo.EventObject} e The event object
31815         */
31816         "click":true,
31817         /**
31818         * @event dblclick
31819         * Fires when this node is double clicked
31820         * @param {Node} this This node
31821         * @param {Roo.EventObject} e The event object
31822         */
31823         "dblclick":true,
31824         /**
31825         * @event contextmenu
31826         * Fires when this node is right clicked
31827         * @param {Node} this This node
31828         * @param {Roo.EventObject} e The event object
31829         */
31830         "contextmenu":true,
31831         /**
31832         * @event beforechildrenrendered
31833         * Fires right before the child nodes for this node are rendered
31834         * @param {Node} this This node
31835         */
31836         "beforechildrenrendered":true
31837     });
31838
31839     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31840
31841     /**
31842      * Read-only. The UI for this node
31843      * @type TreeNodeUI
31844      */
31845     this.ui = new uiClass(this);
31846 };
31847 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31848     preventHScroll: true,
31849     /**
31850      * Returns true if this node is expanded
31851      * @return {Boolean}
31852      */
31853     isExpanded : function(){
31854         return this.expanded;
31855     },
31856
31857     /**
31858      * Returns the UI object for this node
31859      * @return {TreeNodeUI}
31860      */
31861     getUI : function(){
31862         return this.ui;
31863     },
31864
31865     // private override
31866     setFirstChild : function(node){
31867         var of = this.firstChild;
31868         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31869         if(this.childrenRendered && of && node != of){
31870             of.renderIndent(true, true);
31871         }
31872         if(this.rendered){
31873             this.renderIndent(true, true);
31874         }
31875     },
31876
31877     // private override
31878     setLastChild : function(node){
31879         var ol = this.lastChild;
31880         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31881         if(this.childrenRendered && ol && node != ol){
31882             ol.renderIndent(true, true);
31883         }
31884         if(this.rendered){
31885             this.renderIndent(true, true);
31886         }
31887     },
31888
31889     // these methods are overridden to provide lazy rendering support
31890     // private override
31891     appendChild : function(){
31892         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31893         if(node && this.childrenRendered){
31894             node.render();
31895         }
31896         this.ui.updateExpandIcon();
31897         return node;
31898     },
31899
31900     // private override
31901     removeChild : function(node){
31902         this.ownerTree.getSelectionModel().unselect(node);
31903         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31904         // if it's been rendered remove dom node
31905         if(this.childrenRendered){
31906             node.ui.remove();
31907         }
31908         if(this.childNodes.length < 1){
31909             this.collapse(false, false);
31910         }else{
31911             this.ui.updateExpandIcon();
31912         }
31913         if(!this.firstChild) {
31914             this.childrenRendered = false;
31915         }
31916         return node;
31917     },
31918
31919     // private override
31920     insertBefore : function(node, refNode){
31921         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31922         if(newNode && refNode && this.childrenRendered){
31923             node.render();
31924         }
31925         this.ui.updateExpandIcon();
31926         return newNode;
31927     },
31928
31929     /**
31930      * Sets the text for this node
31931      * @param {String} text
31932      */
31933     setText : function(text){
31934         var oldText = this.text;
31935         this.text = text;
31936         this.attributes.text = text;
31937         if(this.rendered){ // event without subscribing
31938             this.ui.onTextChange(this, text, oldText);
31939         }
31940         this.fireEvent("textchange", this, text, oldText);
31941     },
31942
31943     /**
31944      * Triggers selection of this node
31945      */
31946     select : function(){
31947         this.getOwnerTree().getSelectionModel().select(this);
31948     },
31949
31950     /**
31951      * Triggers deselection of this node
31952      */
31953     unselect : function(){
31954         this.getOwnerTree().getSelectionModel().unselect(this);
31955     },
31956
31957     /**
31958      * Returns true if this node is selected
31959      * @return {Boolean}
31960      */
31961     isSelected : function(){
31962         return this.getOwnerTree().getSelectionModel().isSelected(this);
31963     },
31964
31965     /**
31966      * Expand this node.
31967      * @param {Boolean} deep (optional) True to expand all children as well
31968      * @param {Boolean} anim (optional) false to cancel the default animation
31969      * @param {Function} callback (optional) A callback to be called when
31970      * expanding this node completes (does not wait for deep expand to complete).
31971      * Called with 1 parameter, this node.
31972      */
31973     expand : function(deep, anim, callback){
31974         if(!this.expanded){
31975             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31976                 return;
31977             }
31978             if(!this.childrenRendered){
31979                 this.renderChildren();
31980             }
31981             this.expanded = true;
31982             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31983                 this.ui.animExpand(function(){
31984                     this.fireEvent("expand", this);
31985                     if(typeof callback == "function"){
31986                         callback(this);
31987                     }
31988                     if(deep === true){
31989                         this.expandChildNodes(true);
31990                     }
31991                 }.createDelegate(this));
31992                 return;
31993             }else{
31994                 this.ui.expand();
31995                 this.fireEvent("expand", this);
31996                 if(typeof callback == "function"){
31997                     callback(this);
31998                 }
31999             }
32000         }else{
32001            if(typeof callback == "function"){
32002                callback(this);
32003            }
32004         }
32005         if(deep === true){
32006             this.expandChildNodes(true);
32007         }
32008     },
32009
32010     isHiddenRoot : function(){
32011         return this.isRoot && !this.getOwnerTree().rootVisible;
32012     },
32013
32014     /**
32015      * Collapse this node.
32016      * @param {Boolean} deep (optional) True to collapse all children as well
32017      * @param {Boolean} anim (optional) false to cancel the default animation
32018      */
32019     collapse : function(deep, anim){
32020         if(this.expanded && !this.isHiddenRoot()){
32021             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32022                 return;
32023             }
32024             this.expanded = false;
32025             if((this.getOwnerTree().animate && anim !== false) || anim){
32026                 this.ui.animCollapse(function(){
32027                     this.fireEvent("collapse", this);
32028                     if(deep === true){
32029                         this.collapseChildNodes(true);
32030                     }
32031                 }.createDelegate(this));
32032                 return;
32033             }else{
32034                 this.ui.collapse();
32035                 this.fireEvent("collapse", this);
32036             }
32037         }
32038         if(deep === true){
32039             var cs = this.childNodes;
32040             for(var i = 0, len = cs.length; i < len; i++) {
32041                 cs[i].collapse(true, false);
32042             }
32043         }
32044     },
32045
32046     // private
32047     delayedExpand : function(delay){
32048         if(!this.expandProcId){
32049             this.expandProcId = this.expand.defer(delay, this);
32050         }
32051     },
32052
32053     // private
32054     cancelExpand : function(){
32055         if(this.expandProcId){
32056             clearTimeout(this.expandProcId);
32057         }
32058         this.expandProcId = false;
32059     },
32060
32061     /**
32062      * Toggles expanded/collapsed state of the node
32063      */
32064     toggle : function(){
32065         if(this.expanded){
32066             this.collapse();
32067         }else{
32068             this.expand();
32069         }
32070     },
32071
32072     /**
32073      * Ensures all parent nodes are expanded
32074      */
32075     ensureVisible : function(callback){
32076         var tree = this.getOwnerTree();
32077         tree.expandPath(this.parentNode.getPath(), false, function(){
32078             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32079             Roo.callback(callback);
32080         }.createDelegate(this));
32081     },
32082
32083     /**
32084      * Expand all child nodes
32085      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32086      */
32087     expandChildNodes : function(deep){
32088         var cs = this.childNodes;
32089         for(var i = 0, len = cs.length; i < len; i++) {
32090                 cs[i].expand(deep);
32091         }
32092     },
32093
32094     /**
32095      * Collapse all child nodes
32096      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32097      */
32098     collapseChildNodes : function(deep){
32099         var cs = this.childNodes;
32100         for(var i = 0, len = cs.length; i < len; i++) {
32101                 cs[i].collapse(deep);
32102         }
32103     },
32104
32105     /**
32106      * Disables this node
32107      */
32108     disable : function(){
32109         this.disabled = true;
32110         this.unselect();
32111         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32112             this.ui.onDisableChange(this, true);
32113         }
32114         this.fireEvent("disabledchange", this, true);
32115     },
32116
32117     /**
32118      * Enables this node
32119      */
32120     enable : function(){
32121         this.disabled = false;
32122         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32123             this.ui.onDisableChange(this, false);
32124         }
32125         this.fireEvent("disabledchange", this, false);
32126     },
32127
32128     // private
32129     renderChildren : function(suppressEvent){
32130         if(suppressEvent !== false){
32131             this.fireEvent("beforechildrenrendered", this);
32132         }
32133         var cs = this.childNodes;
32134         for(var i = 0, len = cs.length; i < len; i++){
32135             cs[i].render(true);
32136         }
32137         this.childrenRendered = true;
32138     },
32139
32140     // private
32141     sort : function(fn, scope){
32142         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32143         if(this.childrenRendered){
32144             var cs = this.childNodes;
32145             for(var i = 0, len = cs.length; i < len; i++){
32146                 cs[i].render(true);
32147             }
32148         }
32149     },
32150
32151     // private
32152     render : function(bulkRender){
32153         this.ui.render(bulkRender);
32154         if(!this.rendered){
32155             this.rendered = true;
32156             if(this.expanded){
32157                 this.expanded = false;
32158                 this.expand(false, false);
32159             }
32160         }
32161     },
32162
32163     // private
32164     renderIndent : function(deep, refresh){
32165         if(refresh){
32166             this.ui.childIndent = null;
32167         }
32168         this.ui.renderIndent();
32169         if(deep === true && this.childrenRendered){
32170             var cs = this.childNodes;
32171             for(var i = 0, len = cs.length; i < len; i++){
32172                 cs[i].renderIndent(true, refresh);
32173             }
32174         }
32175     }
32176 });/*
32177  * Based on:
32178  * Ext JS Library 1.1.1
32179  * Copyright(c) 2006-2007, Ext JS, LLC.
32180  *
32181  * Originally Released Under LGPL - original licence link has changed is not relivant.
32182  *
32183  * Fork - LGPL
32184  * <script type="text/javascript">
32185  */
32186  
32187 /**
32188  * @class Roo.tree.AsyncTreeNode
32189  * @extends Roo.tree.TreeNode
32190  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32191  * @constructor
32192  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32193  */
32194  Roo.tree.AsyncTreeNode = function(config){
32195     this.loaded = false;
32196     this.loading = false;
32197     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32198     /**
32199     * @event beforeload
32200     * Fires before this node is loaded, return false to cancel
32201     * @param {Node} this This node
32202     */
32203     this.addEvents({'beforeload':true, 'load': true});
32204     /**
32205     * @event load
32206     * Fires when this node is loaded
32207     * @param {Node} this This node
32208     */
32209     /**
32210      * The loader used by this node (defaults to using the tree's defined loader)
32211      * @type TreeLoader
32212      * @property loader
32213      */
32214 };
32215 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32216     expand : function(deep, anim, callback){
32217         if(this.loading){ // if an async load is already running, waiting til it's done
32218             var timer;
32219             var f = function(){
32220                 if(!this.loading){ // done loading
32221                     clearInterval(timer);
32222                     this.expand(deep, anim, callback);
32223                 }
32224             }.createDelegate(this);
32225             timer = setInterval(f, 200);
32226             return;
32227         }
32228         if(!this.loaded){
32229             if(this.fireEvent("beforeload", this) === false){
32230                 return;
32231             }
32232             this.loading = true;
32233             this.ui.beforeLoad(this);
32234             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32235             if(loader){
32236                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32237                 return;
32238             }
32239         }
32240         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32241     },
32242     
32243     /**
32244      * Returns true if this node is currently loading
32245      * @return {Boolean}
32246      */
32247     isLoading : function(){
32248         return this.loading;  
32249     },
32250     
32251     loadComplete : function(deep, anim, callback){
32252         this.loading = false;
32253         this.loaded = true;
32254         this.ui.afterLoad(this);
32255         this.fireEvent("load", this);
32256         this.expand(deep, anim, callback);
32257     },
32258     
32259     /**
32260      * Returns true if this node has been loaded
32261      * @return {Boolean}
32262      */
32263     isLoaded : function(){
32264         return this.loaded;
32265     },
32266     
32267     hasChildNodes : function(){
32268         if(!this.isLeaf() && !this.loaded){
32269             return true;
32270         }else{
32271             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32272         }
32273     },
32274
32275     /**
32276      * Trigger a reload for this node
32277      * @param {Function} callback
32278      */
32279     reload : function(callback){
32280         this.collapse(false, false);
32281         while(this.firstChild){
32282             this.removeChild(this.firstChild);
32283         }
32284         this.childrenRendered = false;
32285         this.loaded = false;
32286         if(this.isHiddenRoot()){
32287             this.expanded = false;
32288         }
32289         this.expand(false, false, callback);
32290     }
32291 });/*
32292  * Based on:
32293  * Ext JS Library 1.1.1
32294  * Copyright(c) 2006-2007, Ext JS, LLC.
32295  *
32296  * Originally Released Under LGPL - original licence link has changed is not relivant.
32297  *
32298  * Fork - LGPL
32299  * <script type="text/javascript">
32300  */
32301  
32302 /**
32303  * @class Roo.tree.TreeNodeUI
32304  * @constructor
32305  * @param {Object} node The node to render
32306  * The TreeNode UI implementation is separate from the
32307  * tree implementation. Unless you are customizing the tree UI,
32308  * you should never have to use this directly.
32309  */
32310 Roo.tree.TreeNodeUI = function(node){
32311     this.node = node;
32312     this.rendered = false;
32313     this.animating = false;
32314     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32315 };
32316
32317 Roo.tree.TreeNodeUI.prototype = {
32318     removeChild : function(node){
32319         if(this.rendered){
32320             this.ctNode.removeChild(node.ui.getEl());
32321         }
32322     },
32323
32324     beforeLoad : function(){
32325          this.addClass("x-tree-node-loading");
32326     },
32327
32328     afterLoad : function(){
32329          this.removeClass("x-tree-node-loading");
32330     },
32331
32332     onTextChange : function(node, text, oldText){
32333         if(this.rendered){
32334             this.textNode.innerHTML = text;
32335         }
32336     },
32337
32338     onDisableChange : function(node, state){
32339         this.disabled = state;
32340         if(state){
32341             this.addClass("x-tree-node-disabled");
32342         }else{
32343             this.removeClass("x-tree-node-disabled");
32344         }
32345     },
32346
32347     onSelectedChange : function(state){
32348         if(state){
32349             this.focus();
32350             this.addClass("x-tree-selected");
32351         }else{
32352             //this.blur();
32353             this.removeClass("x-tree-selected");
32354         }
32355     },
32356
32357     onMove : function(tree, node, oldParent, newParent, index, refNode){
32358         this.childIndent = null;
32359         if(this.rendered){
32360             var targetNode = newParent.ui.getContainer();
32361             if(!targetNode){//target not rendered
32362                 this.holder = document.createElement("div");
32363                 this.holder.appendChild(this.wrap);
32364                 return;
32365             }
32366             var insertBefore = refNode ? refNode.ui.getEl() : null;
32367             if(insertBefore){
32368                 targetNode.insertBefore(this.wrap, insertBefore);
32369             }else{
32370                 targetNode.appendChild(this.wrap);
32371             }
32372             this.node.renderIndent(true);
32373         }
32374     },
32375
32376     addClass : function(cls){
32377         if(this.elNode){
32378             Roo.fly(this.elNode).addClass(cls);
32379         }
32380     },
32381
32382     removeClass : function(cls){
32383         if(this.elNode){
32384             Roo.fly(this.elNode).removeClass(cls);
32385         }
32386     },
32387
32388     remove : function(){
32389         if(this.rendered){
32390             this.holder = document.createElement("div");
32391             this.holder.appendChild(this.wrap);
32392         }
32393     },
32394
32395     fireEvent : function(){
32396         return this.node.fireEvent.apply(this.node, arguments);
32397     },
32398
32399     initEvents : function(){
32400         this.node.on("move", this.onMove, this);
32401         var E = Roo.EventManager;
32402         var a = this.anchor;
32403
32404         var el = Roo.fly(a, '_treeui');
32405
32406         if(Roo.isOpera){ // opera render bug ignores the CSS
32407             el.setStyle("text-decoration", "none");
32408         }
32409
32410         el.on("click", this.onClick, this);
32411         el.on("dblclick", this.onDblClick, this);
32412
32413         if(this.checkbox){
32414             Roo.EventManager.on(this.checkbox,
32415                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32416         }
32417
32418         el.on("contextmenu", this.onContextMenu, this);
32419
32420         var icon = Roo.fly(this.iconNode);
32421         icon.on("click", this.onClick, this);
32422         icon.on("dblclick", this.onDblClick, this);
32423         icon.on("contextmenu", this.onContextMenu, this);
32424         E.on(this.ecNode, "click", this.ecClick, this, true);
32425
32426         if(this.node.disabled){
32427             this.addClass("x-tree-node-disabled");
32428         }
32429         if(this.node.hidden){
32430             this.addClass("x-tree-node-disabled");
32431         }
32432         var ot = this.node.getOwnerTree();
32433         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32434         if(dd && (!this.node.isRoot || ot.rootVisible)){
32435             Roo.dd.Registry.register(this.elNode, {
32436                 node: this.node,
32437                 handles: this.getDDHandles(),
32438                 isHandle: false
32439             });
32440         }
32441     },
32442
32443     getDDHandles : function(){
32444         return [this.iconNode, this.textNode];
32445     },
32446
32447     hide : function(){
32448         if(this.rendered){
32449             this.wrap.style.display = "none";
32450         }
32451     },
32452
32453     show : function(){
32454         if(this.rendered){
32455             this.wrap.style.display = "";
32456         }
32457     },
32458
32459     onContextMenu : function(e){
32460         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32461             e.preventDefault();
32462             this.focus();
32463             this.fireEvent("contextmenu", this.node, e);
32464         }
32465     },
32466
32467     onClick : function(e){
32468         if(this.dropping){
32469             e.stopEvent();
32470             return;
32471         }
32472         if(this.fireEvent("beforeclick", this.node, e) !== false){
32473             if(!this.disabled && this.node.attributes.href){
32474                 this.fireEvent("click", this.node, e);
32475                 return;
32476             }
32477             e.preventDefault();
32478             if(this.disabled){
32479                 return;
32480             }
32481
32482             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32483                 this.node.toggle();
32484             }
32485
32486             this.fireEvent("click", this.node, e);
32487         }else{
32488             e.stopEvent();
32489         }
32490     },
32491
32492     onDblClick : function(e){
32493         e.preventDefault();
32494         if(this.disabled){
32495             return;
32496         }
32497         if(this.checkbox){
32498             this.toggleCheck();
32499         }
32500         if(!this.animating && this.node.hasChildNodes()){
32501             this.node.toggle();
32502         }
32503         this.fireEvent("dblclick", this.node, e);
32504     },
32505
32506     onCheckChange : function(){
32507         var checked = this.checkbox.checked;
32508         this.node.attributes.checked = checked;
32509         this.fireEvent('checkchange', this.node, checked);
32510     },
32511
32512     ecClick : function(e){
32513         if(!this.animating && this.node.hasChildNodes()){
32514             this.node.toggle();
32515         }
32516     },
32517
32518     startDrop : function(){
32519         this.dropping = true;
32520     },
32521
32522     // delayed drop so the click event doesn't get fired on a drop
32523     endDrop : function(){
32524        setTimeout(function(){
32525            this.dropping = false;
32526        }.createDelegate(this), 50);
32527     },
32528
32529     expand : function(){
32530         this.updateExpandIcon();
32531         this.ctNode.style.display = "";
32532     },
32533
32534     focus : function(){
32535         if(!this.node.preventHScroll){
32536             try{this.anchor.focus();
32537             }catch(e){}
32538         }else if(!Roo.isIE){
32539             try{
32540                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32541                 var l = noscroll.scrollLeft;
32542                 this.anchor.focus();
32543                 noscroll.scrollLeft = l;
32544             }catch(e){}
32545         }
32546     },
32547
32548     toggleCheck : function(value){
32549         var cb = this.checkbox;
32550         if(cb){
32551             cb.checked = (value === undefined ? !cb.checked : value);
32552         }
32553     },
32554
32555     blur : function(){
32556         try{
32557             this.anchor.blur();
32558         }catch(e){}
32559     },
32560
32561     animExpand : function(callback){
32562         var ct = Roo.get(this.ctNode);
32563         ct.stopFx();
32564         if(!this.node.hasChildNodes()){
32565             this.updateExpandIcon();
32566             this.ctNode.style.display = "";
32567             Roo.callback(callback);
32568             return;
32569         }
32570         this.animating = true;
32571         this.updateExpandIcon();
32572
32573         ct.slideIn('t', {
32574            callback : function(){
32575                this.animating = false;
32576                Roo.callback(callback);
32577             },
32578             scope: this,
32579             duration: this.node.ownerTree.duration || .25
32580         });
32581     },
32582
32583     highlight : function(){
32584         var tree = this.node.getOwnerTree();
32585         Roo.fly(this.wrap).highlight(
32586             tree.hlColor || "C3DAF9",
32587             {endColor: tree.hlBaseColor}
32588         );
32589     },
32590
32591     collapse : function(){
32592         this.updateExpandIcon();
32593         this.ctNode.style.display = "none";
32594     },
32595
32596     animCollapse : function(callback){
32597         var ct = Roo.get(this.ctNode);
32598         ct.enableDisplayMode('block');
32599         ct.stopFx();
32600
32601         this.animating = true;
32602         this.updateExpandIcon();
32603
32604         ct.slideOut('t', {
32605             callback : function(){
32606                this.animating = false;
32607                Roo.callback(callback);
32608             },
32609             scope: this,
32610             duration: this.node.ownerTree.duration || .25
32611         });
32612     },
32613
32614     getContainer : function(){
32615         return this.ctNode;
32616     },
32617
32618     getEl : function(){
32619         return this.wrap;
32620     },
32621
32622     appendDDGhost : function(ghostNode){
32623         ghostNode.appendChild(this.elNode.cloneNode(true));
32624     },
32625
32626     getDDRepairXY : function(){
32627         return Roo.lib.Dom.getXY(this.iconNode);
32628     },
32629
32630     onRender : function(){
32631         this.render();
32632     },
32633
32634     render : function(bulkRender){
32635         var n = this.node, a = n.attributes;
32636         var targetNode = n.parentNode ?
32637               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32638
32639         if(!this.rendered){
32640             this.rendered = true;
32641
32642             this.renderElements(n, a, targetNode, bulkRender);
32643
32644             if(a.qtip){
32645                if(this.textNode.setAttributeNS){
32646                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32647                    if(a.qtipTitle){
32648                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32649                    }
32650                }else{
32651                    this.textNode.setAttribute("ext:qtip", a.qtip);
32652                    if(a.qtipTitle){
32653                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32654                    }
32655                }
32656             }else if(a.qtipCfg){
32657                 a.qtipCfg.target = Roo.id(this.textNode);
32658                 Roo.QuickTips.register(a.qtipCfg);
32659             }
32660             this.initEvents();
32661             if(!this.node.expanded){
32662                 this.updateExpandIcon();
32663             }
32664         }else{
32665             if(bulkRender === true) {
32666                 targetNode.appendChild(this.wrap);
32667             }
32668         }
32669     },
32670
32671     renderElements : function(n, a, targetNode, bulkRender)
32672     {
32673         // add some indent caching, this helps performance when rendering a large tree
32674         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32675         var t = n.getOwnerTree();
32676         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32677         if (typeof(n.attributes.html) != 'undefined') {
32678             txt = n.attributes.html;
32679         }
32680         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32681         var cb = typeof a.checked == 'boolean';
32682         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32683         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32684             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32685             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32686             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32687             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32688             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32689              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32690                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32691             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32692             "</li>"];
32693
32694         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32695             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32696                                 n.nextSibling.ui.getEl(), buf.join(""));
32697         }else{
32698             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32699         }
32700
32701         this.elNode = this.wrap.childNodes[0];
32702         this.ctNode = this.wrap.childNodes[1];
32703         var cs = this.elNode.childNodes;
32704         this.indentNode = cs[0];
32705         this.ecNode = cs[1];
32706         this.iconNode = cs[2];
32707         var index = 3;
32708         if(cb){
32709             this.checkbox = cs[3];
32710             index++;
32711         }
32712         this.anchor = cs[index];
32713         this.textNode = cs[index].firstChild;
32714     },
32715
32716     getAnchor : function(){
32717         return this.anchor;
32718     },
32719
32720     getTextEl : function(){
32721         return this.textNode;
32722     },
32723
32724     getIconEl : function(){
32725         return this.iconNode;
32726     },
32727
32728     isChecked : function(){
32729         return this.checkbox ? this.checkbox.checked : false;
32730     },
32731
32732     updateExpandIcon : function(){
32733         if(this.rendered){
32734             var n = this.node, c1, c2;
32735             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32736             var hasChild = n.hasChildNodes();
32737             if(hasChild){
32738                 if(n.expanded){
32739                     cls += "-minus";
32740                     c1 = "x-tree-node-collapsed";
32741                     c2 = "x-tree-node-expanded";
32742                 }else{
32743                     cls += "-plus";
32744                     c1 = "x-tree-node-expanded";
32745                     c2 = "x-tree-node-collapsed";
32746                 }
32747                 if(this.wasLeaf){
32748                     this.removeClass("x-tree-node-leaf");
32749                     this.wasLeaf = false;
32750                 }
32751                 if(this.c1 != c1 || this.c2 != c2){
32752                     Roo.fly(this.elNode).replaceClass(c1, c2);
32753                     this.c1 = c1; this.c2 = c2;
32754                 }
32755             }else{
32756                 // this changes non-leafs into leafs if they have no children.
32757                 // it's not very rational behaviour..
32758                 
32759                 if(!this.wasLeaf && this.node.leaf){
32760                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32761                     delete this.c1;
32762                     delete this.c2;
32763                     this.wasLeaf = true;
32764                 }
32765             }
32766             var ecc = "x-tree-ec-icon "+cls;
32767             if(this.ecc != ecc){
32768                 this.ecNode.className = ecc;
32769                 this.ecc = ecc;
32770             }
32771         }
32772     },
32773
32774     getChildIndent : function(){
32775         if(!this.childIndent){
32776             var buf = [];
32777             var p = this.node;
32778             while(p){
32779                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32780                     if(!p.isLast()) {
32781                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32782                     } else {
32783                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32784                     }
32785                 }
32786                 p = p.parentNode;
32787             }
32788             this.childIndent = buf.join("");
32789         }
32790         return this.childIndent;
32791     },
32792
32793     renderIndent : function(){
32794         if(this.rendered){
32795             var indent = "";
32796             var p = this.node.parentNode;
32797             if(p){
32798                 indent = p.ui.getChildIndent();
32799             }
32800             if(this.indentMarkup != indent){ // don't rerender if not required
32801                 this.indentNode.innerHTML = indent;
32802                 this.indentMarkup = indent;
32803             }
32804             this.updateExpandIcon();
32805         }
32806     }
32807 };
32808
32809 Roo.tree.RootTreeNodeUI = function(){
32810     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32811 };
32812 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32813     render : function(){
32814         if(!this.rendered){
32815             var targetNode = this.node.ownerTree.innerCt.dom;
32816             this.node.expanded = true;
32817             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32818             this.wrap = this.ctNode = targetNode.firstChild;
32819         }
32820     },
32821     collapse : function(){
32822     },
32823     expand : function(){
32824     }
32825 });/*
32826  * Based on:
32827  * Ext JS Library 1.1.1
32828  * Copyright(c) 2006-2007, Ext JS, LLC.
32829  *
32830  * Originally Released Under LGPL - original licence link has changed is not relivant.
32831  *
32832  * Fork - LGPL
32833  * <script type="text/javascript">
32834  */
32835 /**
32836  * @class Roo.tree.TreeLoader
32837  * @extends Roo.util.Observable
32838  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32839  * nodes from a specified URL. The response must be a javascript Array definition
32840  * who's elements are node definition objects. eg:
32841  * <pre><code>
32842    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32843     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32844 </code></pre>
32845  * <br><br>
32846  * A server request is sent, and child nodes are loaded only when a node is expanded.
32847  * The loading node's id is passed to the server under the parameter name "node" to
32848  * enable the server to produce the correct child nodes.
32849  * <br><br>
32850  * To pass extra parameters, an event handler may be attached to the "beforeload"
32851  * event, and the parameters specified in the TreeLoader's baseParams property:
32852  * <pre><code>
32853     myTreeLoader.on("beforeload", function(treeLoader, node) {
32854         this.baseParams.category = node.attributes.category;
32855     }, this);
32856 </code></pre><
32857  * This would pass an HTTP parameter called "category" to the server containing
32858  * the value of the Node's "category" attribute.
32859  * @constructor
32860  * Creates a new Treeloader.
32861  * @param {Object} config A config object containing config properties.
32862  */
32863 Roo.tree.TreeLoader = function(config){
32864     this.baseParams = {};
32865     this.requestMethod = "POST";
32866     Roo.apply(this, config);
32867
32868     this.addEvents({
32869     
32870         /**
32871          * @event beforeload
32872          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32873          * @param {Object} This TreeLoader object.
32874          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32875          * @param {Object} callback The callback function specified in the {@link #load} call.
32876          */
32877         beforeload : true,
32878         /**
32879          * @event load
32880          * Fires when the node has been successfuly loaded.
32881          * @param {Object} This TreeLoader object.
32882          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32883          * @param {Object} response The response object containing the data from the server.
32884          */
32885         load : true,
32886         /**
32887          * @event loadexception
32888          * Fires if the network request failed.
32889          * @param {Object} This TreeLoader object.
32890          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32891          * @param {Object} response The response object containing the data from the server.
32892          */
32893         loadexception : true,
32894         /**
32895          * @event create
32896          * Fires before a node is created, enabling you to return custom Node types 
32897          * @param {Object} This TreeLoader object.
32898          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32899          */
32900         create : true
32901     });
32902
32903     Roo.tree.TreeLoader.superclass.constructor.call(this);
32904 };
32905
32906 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32907     /**
32908     * @cfg {String} dataUrl The URL from which to request a Json string which
32909     * specifies an array of node definition object representing the child nodes
32910     * to be loaded.
32911     */
32912     /**
32913     * @cfg {Object} baseParams (optional) An object containing properties which
32914     * specify HTTP parameters to be passed to each request for child nodes.
32915     */
32916     /**
32917     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32918     * created by this loader. If the attributes sent by the server have an attribute in this object,
32919     * they take priority.
32920     */
32921     /**
32922     * @cfg {Object} uiProviders (optional) An object containing properties which
32923     * 
32924     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32925     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32926     * <i>uiProvider</i> attribute of a returned child node is a string rather
32927     * than a reference to a TreeNodeUI implementation, this that string value
32928     * is used as a property name in the uiProviders object. You can define the provider named
32929     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32930     */
32931     uiProviders : {},
32932
32933     /**
32934     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32935     * child nodes before loading.
32936     */
32937     clearOnLoad : true,
32938
32939     /**
32940     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32941     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32942     * Grid query { data : [ .....] }
32943     */
32944     
32945     root : false,
32946      /**
32947     * @cfg {String} queryParam (optional) 
32948     * Name of the query as it will be passed on the querystring (defaults to 'node')
32949     * eg. the request will be ?node=[id]
32950     */
32951     
32952     
32953     queryParam: false,
32954     
32955     /**
32956      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32957      * This is called automatically when a node is expanded, but may be used to reload
32958      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32959      * @param {Roo.tree.TreeNode} node
32960      * @param {Function} callback
32961      */
32962     load : function(node, callback){
32963         if(this.clearOnLoad){
32964             while(node.firstChild){
32965                 node.removeChild(node.firstChild);
32966             }
32967         }
32968         if(node.attributes.children){ // preloaded json children
32969             var cs = node.attributes.children;
32970             for(var i = 0, len = cs.length; i < len; i++){
32971                 node.appendChild(this.createNode(cs[i]));
32972             }
32973             if(typeof callback == "function"){
32974                 callback();
32975             }
32976         }else if(this.dataUrl){
32977             this.requestData(node, callback);
32978         }
32979     },
32980
32981     getParams: function(node){
32982         var buf = [], bp = this.baseParams;
32983         for(var key in bp){
32984             if(typeof bp[key] != "function"){
32985                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32986             }
32987         }
32988         var n = this.queryParam === false ? 'node' : this.queryParam;
32989         buf.push(n + "=", encodeURIComponent(node.id));
32990         return buf.join("");
32991     },
32992
32993     requestData : function(node, callback){
32994         if(this.fireEvent("beforeload", this, node, callback) !== false){
32995             this.transId = Roo.Ajax.request({
32996                 method:this.requestMethod,
32997                 url: this.dataUrl||this.url,
32998                 success: this.handleResponse,
32999                 failure: this.handleFailure,
33000                 scope: this,
33001                 argument: {callback: callback, node: node},
33002                 params: this.getParams(node)
33003             });
33004         }else{
33005             // if the load is cancelled, make sure we notify
33006             // the node that we are done
33007             if(typeof callback == "function"){
33008                 callback();
33009             }
33010         }
33011     },
33012
33013     isLoading : function(){
33014         return this.transId ? true : false;
33015     },
33016
33017     abort : function(){
33018         if(this.isLoading()){
33019             Roo.Ajax.abort(this.transId);
33020         }
33021     },
33022
33023     // private
33024     createNode : function(attr)
33025     {
33026         // apply baseAttrs, nice idea Corey!
33027         if(this.baseAttrs){
33028             Roo.applyIf(attr, this.baseAttrs);
33029         }
33030         if(this.applyLoader !== false){
33031             attr.loader = this;
33032         }
33033         // uiProvider = depreciated..
33034         
33035         if(typeof(attr.uiProvider) == 'string'){
33036            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33037                 /**  eval:var:attr */ eval(attr.uiProvider);
33038         }
33039         if(typeof(this.uiProviders['default']) != 'undefined') {
33040             attr.uiProvider = this.uiProviders['default'];
33041         }
33042         
33043         this.fireEvent('create', this, attr);
33044         
33045         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33046         return(attr.leaf ?
33047                         new Roo.tree.TreeNode(attr) :
33048                         new Roo.tree.AsyncTreeNode(attr));
33049     },
33050
33051     processResponse : function(response, node, callback)
33052     {
33053         var json = response.responseText;
33054         try {
33055             
33056             var o = Roo.decode(json);
33057             
33058             if (!o.success) {
33059                 // it's a failure condition.
33060                 var a = response.argument;
33061                 this.fireEvent("loadexception", this, a.node, response);
33062                 Roo.log("Load failed - should have a handler really");
33063                 return;
33064             }
33065             
33066             if (this.root !== false) {
33067                 o = o[this.root];
33068             }
33069             
33070             for(var i = 0, len = o.length; i < len; i++){
33071                 var n = this.createNode(o[i]);
33072                 if(n){
33073                     node.appendChild(n);
33074                 }
33075             }
33076             if(typeof callback == "function"){
33077                 callback(this, node);
33078             }
33079         }catch(e){
33080             this.handleFailure(response);
33081         }
33082     },
33083
33084     handleResponse : function(response){
33085         this.transId = false;
33086         var a = response.argument;
33087         this.processResponse(response, a.node, a.callback);
33088         this.fireEvent("load", this, a.node, response);
33089     },
33090
33091     handleFailure : function(response)
33092     {
33093         // should handle failure better..
33094         this.transId = false;
33095         var a = response.argument;
33096         this.fireEvent("loadexception", this, a.node, response);
33097         if(typeof a.callback == "function"){
33098             a.callback(this, a.node);
33099         }
33100     }
33101 });/*
33102  * Based on:
33103  * Ext JS Library 1.1.1
33104  * Copyright(c) 2006-2007, Ext JS, LLC.
33105  *
33106  * Originally Released Under LGPL - original licence link has changed is not relivant.
33107  *
33108  * Fork - LGPL
33109  * <script type="text/javascript">
33110  */
33111
33112 /**
33113 * @class Roo.tree.TreeFilter
33114 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33115 * @param {TreePanel} tree
33116 * @param {Object} config (optional)
33117  */
33118 Roo.tree.TreeFilter = function(tree, config){
33119     this.tree = tree;
33120     this.filtered = {};
33121     Roo.apply(this, config);
33122 };
33123
33124 Roo.tree.TreeFilter.prototype = {
33125     clearBlank:false,
33126     reverse:false,
33127     autoClear:false,
33128     remove:false,
33129
33130      /**
33131      * Filter the data by a specific attribute.
33132      * @param {String/RegExp} value Either string that the attribute value
33133      * should start with or a RegExp to test against the attribute
33134      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33135      * @param {TreeNode} startNode (optional) The node to start the filter at.
33136      */
33137     filter : function(value, attr, startNode){
33138         attr = attr || "text";
33139         var f;
33140         if(typeof value == "string"){
33141             var vlen = value.length;
33142             // auto clear empty filter
33143             if(vlen == 0 && this.clearBlank){
33144                 this.clear();
33145                 return;
33146             }
33147             value = value.toLowerCase();
33148             f = function(n){
33149                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33150             };
33151         }else if(value.exec){ // regex?
33152             f = function(n){
33153                 return value.test(n.attributes[attr]);
33154             };
33155         }else{
33156             throw 'Illegal filter type, must be string or regex';
33157         }
33158         this.filterBy(f, null, startNode);
33159         },
33160
33161     /**
33162      * Filter by a function. The passed function will be called with each
33163      * node in the tree (or from the startNode). If the function returns true, the node is kept
33164      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33165      * @param {Function} fn The filter function
33166      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33167      */
33168     filterBy : function(fn, scope, startNode){
33169         startNode = startNode || this.tree.root;
33170         if(this.autoClear){
33171             this.clear();
33172         }
33173         var af = this.filtered, rv = this.reverse;
33174         var f = function(n){
33175             if(n == startNode){
33176                 return true;
33177             }
33178             if(af[n.id]){
33179                 return false;
33180             }
33181             var m = fn.call(scope || n, n);
33182             if(!m || rv){
33183                 af[n.id] = n;
33184                 n.ui.hide();
33185                 return false;
33186             }
33187             return true;
33188         };
33189         startNode.cascade(f);
33190         if(this.remove){
33191            for(var id in af){
33192                if(typeof id != "function"){
33193                    var n = af[id];
33194                    if(n && n.parentNode){
33195                        n.parentNode.removeChild(n);
33196                    }
33197                }
33198            }
33199         }
33200     },
33201
33202     /**
33203      * Clears the current filter. Note: with the "remove" option
33204      * set a filter cannot be cleared.
33205      */
33206     clear : function(){
33207         var t = this.tree;
33208         var af = this.filtered;
33209         for(var id in af){
33210             if(typeof id != "function"){
33211                 var n = af[id];
33212                 if(n){
33213                     n.ui.show();
33214                 }
33215             }
33216         }
33217         this.filtered = {};
33218     }
33219 };
33220 /*
33221  * Based on:
33222  * Ext JS Library 1.1.1
33223  * Copyright(c) 2006-2007, Ext JS, LLC.
33224  *
33225  * Originally Released Under LGPL - original licence link has changed is not relivant.
33226  *
33227  * Fork - LGPL
33228  * <script type="text/javascript">
33229  */
33230  
33231
33232 /**
33233  * @class Roo.tree.TreeSorter
33234  * Provides sorting of nodes in a TreePanel
33235  * 
33236  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33237  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33238  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33239  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33240  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33241  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33242  * @constructor
33243  * @param {TreePanel} tree
33244  * @param {Object} config
33245  */
33246 Roo.tree.TreeSorter = function(tree, config){
33247     Roo.apply(this, config);
33248     tree.on("beforechildrenrendered", this.doSort, this);
33249     tree.on("append", this.updateSort, this);
33250     tree.on("insert", this.updateSort, this);
33251     
33252     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33253     var p = this.property || "text";
33254     var sortType = this.sortType;
33255     var fs = this.folderSort;
33256     var cs = this.caseSensitive === true;
33257     var leafAttr = this.leafAttr || 'leaf';
33258
33259     this.sortFn = function(n1, n2){
33260         if(fs){
33261             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33262                 return 1;
33263             }
33264             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33265                 return -1;
33266             }
33267         }
33268         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33269         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33270         if(v1 < v2){
33271                         return dsc ? +1 : -1;
33272                 }else if(v1 > v2){
33273                         return dsc ? -1 : +1;
33274         }else{
33275                 return 0;
33276         }
33277     };
33278 };
33279
33280 Roo.tree.TreeSorter.prototype = {
33281     doSort : function(node){
33282         node.sort(this.sortFn);
33283     },
33284     
33285     compareNodes : function(n1, n2){
33286         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33287     },
33288     
33289     updateSort : function(tree, node){
33290         if(node.childrenRendered){
33291             this.doSort.defer(1, this, [node]);
33292         }
33293     }
33294 };/*
33295  * Based on:
33296  * Ext JS Library 1.1.1
33297  * Copyright(c) 2006-2007, Ext JS, LLC.
33298  *
33299  * Originally Released Under LGPL - original licence link has changed is not relivant.
33300  *
33301  * Fork - LGPL
33302  * <script type="text/javascript">
33303  */
33304
33305 if(Roo.dd.DropZone){
33306     
33307 Roo.tree.TreeDropZone = function(tree, config){
33308     this.allowParentInsert = false;
33309     this.allowContainerDrop = false;
33310     this.appendOnly = false;
33311     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33312     this.tree = tree;
33313     this.lastInsertClass = "x-tree-no-status";
33314     this.dragOverData = {};
33315 };
33316
33317 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33318     ddGroup : "TreeDD",
33319     
33320     expandDelay : 1000,
33321     
33322     expandNode : function(node){
33323         if(node.hasChildNodes() && !node.isExpanded()){
33324             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33325         }
33326     },
33327     
33328     queueExpand : function(node){
33329         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33330     },
33331     
33332     cancelExpand : function(){
33333         if(this.expandProcId){
33334             clearTimeout(this.expandProcId);
33335             this.expandProcId = false;
33336         }
33337     },
33338     
33339     isValidDropPoint : function(n, pt, dd, e, data){
33340         if(!n || !data){ return false; }
33341         var targetNode = n.node;
33342         var dropNode = data.node;
33343         // default drop rules
33344         if(!(targetNode && targetNode.isTarget && pt)){
33345             return false;
33346         }
33347         if(pt == "append" && targetNode.allowChildren === false){
33348             return false;
33349         }
33350         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33351             return false;
33352         }
33353         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33354             return false;
33355         }
33356         // reuse the object
33357         var overEvent = this.dragOverData;
33358         overEvent.tree = this.tree;
33359         overEvent.target = targetNode;
33360         overEvent.data = data;
33361         overEvent.point = pt;
33362         overEvent.source = dd;
33363         overEvent.rawEvent = e;
33364         overEvent.dropNode = dropNode;
33365         overEvent.cancel = false;  
33366         var result = this.tree.fireEvent("nodedragover", overEvent);
33367         return overEvent.cancel === false && result !== false;
33368     },
33369     
33370     getDropPoint : function(e, n, dd){
33371         var tn = n.node;
33372         if(tn.isRoot){
33373             return tn.allowChildren !== false ? "append" : false; // always append for root
33374         }
33375         var dragEl = n.ddel;
33376         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33377         var y = Roo.lib.Event.getPageY(e);
33378         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33379         
33380         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33381         var noAppend = tn.allowChildren === false;
33382         if(this.appendOnly || tn.parentNode.allowChildren === false){
33383             return noAppend ? false : "append";
33384         }
33385         var noBelow = false;
33386         if(!this.allowParentInsert){
33387             noBelow = tn.hasChildNodes() && tn.isExpanded();
33388         }
33389         var q = (b - t) / (noAppend ? 2 : 3);
33390         if(y >= t && y < (t + q)){
33391             return "above";
33392         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33393             return "below";
33394         }else{
33395             return "append";
33396         }
33397     },
33398     
33399     onNodeEnter : function(n, dd, e, data){
33400         this.cancelExpand();
33401     },
33402     
33403     onNodeOver : function(n, dd, e, data){
33404         var pt = this.getDropPoint(e, n, dd);
33405         var node = n.node;
33406         
33407         // auto node expand check
33408         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33409             this.queueExpand(node);
33410         }else if(pt != "append"){
33411             this.cancelExpand();
33412         }
33413         
33414         // set the insert point style on the target node
33415         var returnCls = this.dropNotAllowed;
33416         if(this.isValidDropPoint(n, pt, dd, e, data)){
33417            if(pt){
33418                var el = n.ddel;
33419                var cls;
33420                if(pt == "above"){
33421                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33422                    cls = "x-tree-drag-insert-above";
33423                }else if(pt == "below"){
33424                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33425                    cls = "x-tree-drag-insert-below";
33426                }else{
33427                    returnCls = "x-tree-drop-ok-append";
33428                    cls = "x-tree-drag-append";
33429                }
33430                if(this.lastInsertClass != cls){
33431                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33432                    this.lastInsertClass = cls;
33433                }
33434            }
33435        }
33436        return returnCls;
33437     },
33438     
33439     onNodeOut : function(n, dd, e, data){
33440         this.cancelExpand();
33441         this.removeDropIndicators(n);
33442     },
33443     
33444     onNodeDrop : function(n, dd, e, data){
33445         var point = this.getDropPoint(e, n, dd);
33446         var targetNode = n.node;
33447         targetNode.ui.startDrop();
33448         if(!this.isValidDropPoint(n, point, dd, e, data)){
33449             targetNode.ui.endDrop();
33450             return false;
33451         }
33452         // first try to find the drop node
33453         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33454         var dropEvent = {
33455             tree : this.tree,
33456             target: targetNode,
33457             data: data,
33458             point: point,
33459             source: dd,
33460             rawEvent: e,
33461             dropNode: dropNode,
33462             cancel: !dropNode   
33463         };
33464         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33465         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33466             targetNode.ui.endDrop();
33467             return false;
33468         }
33469         // allow target changing
33470         targetNode = dropEvent.target;
33471         if(point == "append" && !targetNode.isExpanded()){
33472             targetNode.expand(false, null, function(){
33473                 this.completeDrop(dropEvent);
33474             }.createDelegate(this));
33475         }else{
33476             this.completeDrop(dropEvent);
33477         }
33478         return true;
33479     },
33480     
33481     completeDrop : function(de){
33482         var ns = de.dropNode, p = de.point, t = de.target;
33483         if(!(ns instanceof Array)){
33484             ns = [ns];
33485         }
33486         var n;
33487         for(var i = 0, len = ns.length; i < len; i++){
33488             n = ns[i];
33489             if(p == "above"){
33490                 t.parentNode.insertBefore(n, t);
33491             }else if(p == "below"){
33492                 t.parentNode.insertBefore(n, t.nextSibling);
33493             }else{
33494                 t.appendChild(n);
33495             }
33496         }
33497         n.ui.focus();
33498         if(this.tree.hlDrop){
33499             n.ui.highlight();
33500         }
33501         t.ui.endDrop();
33502         this.tree.fireEvent("nodedrop", de);
33503     },
33504     
33505     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33506         if(this.tree.hlDrop){
33507             dropNode.ui.focus();
33508             dropNode.ui.highlight();
33509         }
33510         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33511     },
33512     
33513     getTree : function(){
33514         return this.tree;
33515     },
33516     
33517     removeDropIndicators : function(n){
33518         if(n && n.ddel){
33519             var el = n.ddel;
33520             Roo.fly(el).removeClass([
33521                     "x-tree-drag-insert-above",
33522                     "x-tree-drag-insert-below",
33523                     "x-tree-drag-append"]);
33524             this.lastInsertClass = "_noclass";
33525         }
33526     },
33527     
33528     beforeDragDrop : function(target, e, id){
33529         this.cancelExpand();
33530         return true;
33531     },
33532     
33533     afterRepair : function(data){
33534         if(data && Roo.enableFx){
33535             data.node.ui.highlight();
33536         }
33537         this.hideProxy();
33538     }    
33539 });
33540
33541 }
33542 /*
33543  * Based on:
33544  * Ext JS Library 1.1.1
33545  * Copyright(c) 2006-2007, Ext JS, LLC.
33546  *
33547  * Originally Released Under LGPL - original licence link has changed is not relivant.
33548  *
33549  * Fork - LGPL
33550  * <script type="text/javascript">
33551  */
33552  
33553
33554 if(Roo.dd.DragZone){
33555 Roo.tree.TreeDragZone = function(tree, config){
33556     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33557     this.tree = tree;
33558 };
33559
33560 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33561     ddGroup : "TreeDD",
33562     
33563     onBeforeDrag : function(data, e){
33564         var n = data.node;
33565         return n && n.draggable && !n.disabled;
33566     },
33567     
33568     onInitDrag : function(e){
33569         var data = this.dragData;
33570         this.tree.getSelectionModel().select(data.node);
33571         this.proxy.update("");
33572         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33573         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33574     },
33575     
33576     getRepairXY : function(e, data){
33577         return data.node.ui.getDDRepairXY();
33578     },
33579     
33580     onEndDrag : function(data, e){
33581         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33582     },
33583     
33584     onValidDrop : function(dd, e, id){
33585         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33586         this.hideProxy();
33587     },
33588     
33589     beforeInvalidDrop : function(e, id){
33590         // this scrolls the original position back into view
33591         var sm = this.tree.getSelectionModel();
33592         sm.clearSelections();
33593         sm.select(this.dragData.node);
33594     }
33595 });
33596 }/*
33597  * Based on:
33598  * Ext JS Library 1.1.1
33599  * Copyright(c) 2006-2007, Ext JS, LLC.
33600  *
33601  * Originally Released Under LGPL - original licence link has changed is not relivant.
33602  *
33603  * Fork - LGPL
33604  * <script type="text/javascript">
33605  */
33606 /**
33607  * @class Roo.tree.TreeEditor
33608  * @extends Roo.Editor
33609  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33610  * as the editor field.
33611  * @constructor
33612  * @param {Object} config (used to be the tree panel.)
33613  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33614  * 
33615  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33616  * @cfg {Roo.form.TextField|Object} field The field configuration
33617  *
33618  * 
33619  */
33620 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33621     var tree = config;
33622     var field;
33623     if (oldconfig) { // old style..
33624         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33625     } else {
33626         // new style..
33627         tree = config.tree;
33628         config.field = config.field  || {};
33629         config.field.xtype = 'TextField';
33630         field = Roo.factory(config.field, Roo.form);
33631     }
33632     config = config || {};
33633     
33634     
33635     this.addEvents({
33636         /**
33637          * @event beforenodeedit
33638          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33639          * false from the handler of this event.
33640          * @param {Editor} this
33641          * @param {Roo.tree.Node} node 
33642          */
33643         "beforenodeedit" : true
33644     });
33645     
33646     //Roo.log(config);
33647     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33648
33649     this.tree = tree;
33650
33651     tree.on('beforeclick', this.beforeNodeClick, this);
33652     tree.getTreeEl().on('mousedown', this.hide, this);
33653     this.on('complete', this.updateNode, this);
33654     this.on('beforestartedit', this.fitToTree, this);
33655     this.on('startedit', this.bindScroll, this, {delay:10});
33656     this.on('specialkey', this.onSpecialKey, this);
33657 };
33658
33659 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33660     /**
33661      * @cfg {String} alignment
33662      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33663      */
33664     alignment: "l-l",
33665     // inherit
33666     autoSize: false,
33667     /**
33668      * @cfg {Boolean} hideEl
33669      * True to hide the bound element while the editor is displayed (defaults to false)
33670      */
33671     hideEl : false,
33672     /**
33673      * @cfg {String} cls
33674      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33675      */
33676     cls: "x-small-editor x-tree-editor",
33677     /**
33678      * @cfg {Boolean} shim
33679      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33680      */
33681     shim:false,
33682     // inherit
33683     shadow:"frame",
33684     /**
33685      * @cfg {Number} maxWidth
33686      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33687      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33688      * scroll and client offsets into account prior to each edit.
33689      */
33690     maxWidth: 250,
33691
33692     editDelay : 350,
33693
33694     // private
33695     fitToTree : function(ed, el){
33696         var td = this.tree.getTreeEl().dom, nd = el.dom;
33697         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33698             td.scrollLeft = nd.offsetLeft;
33699         }
33700         var w = Math.min(
33701                 this.maxWidth,
33702                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33703         this.setSize(w, '');
33704         
33705         return this.fireEvent('beforenodeedit', this, this.editNode);
33706         
33707     },
33708
33709     // private
33710     triggerEdit : function(node){
33711         this.completeEdit();
33712         this.editNode = node;
33713         this.startEdit(node.ui.textNode, node.text);
33714     },
33715
33716     // private
33717     bindScroll : function(){
33718         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33719     },
33720
33721     // private
33722     beforeNodeClick : function(node, e){
33723         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33724         this.lastClick = new Date();
33725         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33726             e.stopEvent();
33727             this.triggerEdit(node);
33728             return false;
33729         }
33730         return true;
33731     },
33732
33733     // private
33734     updateNode : function(ed, value){
33735         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33736         this.editNode.setText(value);
33737     },
33738
33739     // private
33740     onHide : function(){
33741         Roo.tree.TreeEditor.superclass.onHide.call(this);
33742         if(this.editNode){
33743             this.editNode.ui.focus();
33744         }
33745     },
33746
33747     // private
33748     onSpecialKey : function(field, e){
33749         var k = e.getKey();
33750         if(k == e.ESC){
33751             e.stopEvent();
33752             this.cancelEdit();
33753         }else if(k == e.ENTER && !e.hasModifier()){
33754             e.stopEvent();
33755             this.completeEdit();
33756         }
33757     }
33758 });//<Script type="text/javascript">
33759 /*
33760  * Based on:
33761  * Ext JS Library 1.1.1
33762  * Copyright(c) 2006-2007, Ext JS, LLC.
33763  *
33764  * Originally Released Under LGPL - original licence link has changed is not relivant.
33765  *
33766  * Fork - LGPL
33767  * <script type="text/javascript">
33768  */
33769  
33770 /**
33771  * Not documented??? - probably should be...
33772  */
33773
33774 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33775     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33776     
33777     renderElements : function(n, a, targetNode, bulkRender){
33778         //consel.log("renderElements?");
33779         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33780
33781         var t = n.getOwnerTree();
33782         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33783         
33784         var cols = t.columns;
33785         var bw = t.borderWidth;
33786         var c = cols[0];
33787         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33788          var cb = typeof a.checked == "boolean";
33789         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33790         var colcls = 'x-t-' + tid + '-c0';
33791         var buf = [
33792             '<li class="x-tree-node">',
33793             
33794                 
33795                 '<div class="x-tree-node-el ', a.cls,'">',
33796                     // extran...
33797                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33798                 
33799                 
33800                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33801                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33802                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33803                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33804                            (a.iconCls ? ' '+a.iconCls : ''),
33805                            '" unselectable="on" />',
33806                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33807                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33808                              
33809                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33810                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33811                             '<span unselectable="on" qtip="' + tx + '">',
33812                              tx,
33813                              '</span></a>' ,
33814                     '</div>',
33815                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33816                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33817                  ];
33818         for(var i = 1, len = cols.length; i < len; i++){
33819             c = cols[i];
33820             colcls = 'x-t-' + tid + '-c' +i;
33821             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33822             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33823                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33824                       "</div>");
33825          }
33826          
33827          buf.push(
33828             '</a>',
33829             '<div class="x-clear"></div></div>',
33830             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33831             "</li>");
33832         
33833         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33834             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33835                                 n.nextSibling.ui.getEl(), buf.join(""));
33836         }else{
33837             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33838         }
33839         var el = this.wrap.firstChild;
33840         this.elRow = el;
33841         this.elNode = el.firstChild;
33842         this.ranchor = el.childNodes[1];
33843         this.ctNode = this.wrap.childNodes[1];
33844         var cs = el.firstChild.childNodes;
33845         this.indentNode = cs[0];
33846         this.ecNode = cs[1];
33847         this.iconNode = cs[2];
33848         var index = 3;
33849         if(cb){
33850             this.checkbox = cs[3];
33851             index++;
33852         }
33853         this.anchor = cs[index];
33854         
33855         this.textNode = cs[index].firstChild;
33856         
33857         //el.on("click", this.onClick, this);
33858         //el.on("dblclick", this.onDblClick, this);
33859         
33860         
33861        // console.log(this);
33862     },
33863     initEvents : function(){
33864         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33865         
33866             
33867         var a = this.ranchor;
33868
33869         var el = Roo.get(a);
33870
33871         if(Roo.isOpera){ // opera render bug ignores the CSS
33872             el.setStyle("text-decoration", "none");
33873         }
33874
33875         el.on("click", this.onClick, this);
33876         el.on("dblclick", this.onDblClick, this);
33877         el.on("contextmenu", this.onContextMenu, this);
33878         
33879     },
33880     
33881     /*onSelectedChange : function(state){
33882         if(state){
33883             this.focus();
33884             this.addClass("x-tree-selected");
33885         }else{
33886             //this.blur();
33887             this.removeClass("x-tree-selected");
33888         }
33889     },*/
33890     addClass : function(cls){
33891         if(this.elRow){
33892             Roo.fly(this.elRow).addClass(cls);
33893         }
33894         
33895     },
33896     
33897     
33898     removeClass : function(cls){
33899         if(this.elRow){
33900             Roo.fly(this.elRow).removeClass(cls);
33901         }
33902     }
33903
33904     
33905     
33906 });//<Script type="text/javascript">
33907
33908 /*
33909  * Based on:
33910  * Ext JS Library 1.1.1
33911  * Copyright(c) 2006-2007, Ext JS, LLC.
33912  *
33913  * Originally Released Under LGPL - original licence link has changed is not relivant.
33914  *
33915  * Fork - LGPL
33916  * <script type="text/javascript">
33917  */
33918  
33919
33920 /**
33921  * @class Roo.tree.ColumnTree
33922  * @extends Roo.data.TreePanel
33923  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33924  * @cfg {int} borderWidth  compined right/left border allowance
33925  * @constructor
33926  * @param {String/HTMLElement/Element} el The container element
33927  * @param {Object} config
33928  */
33929 Roo.tree.ColumnTree =  function(el, config)
33930 {
33931    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33932    this.addEvents({
33933         /**
33934         * @event resize
33935         * Fire this event on a container when it resizes
33936         * @param {int} w Width
33937         * @param {int} h Height
33938         */
33939        "resize" : true
33940     });
33941     this.on('resize', this.onResize, this);
33942 };
33943
33944 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33945     //lines:false,
33946     
33947     
33948     borderWidth: Roo.isBorderBox ? 0 : 2, 
33949     headEls : false,
33950     
33951     render : function(){
33952         // add the header.....
33953        
33954         Roo.tree.ColumnTree.superclass.render.apply(this);
33955         
33956         this.el.addClass('x-column-tree');
33957         
33958         this.headers = this.el.createChild(
33959             {cls:'x-tree-headers'},this.innerCt.dom);
33960    
33961         var cols = this.columns, c;
33962         var totalWidth = 0;
33963         this.headEls = [];
33964         var  len = cols.length;
33965         for(var i = 0; i < len; i++){
33966              c = cols[i];
33967              totalWidth += c.width;
33968             this.headEls.push(this.headers.createChild({
33969                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33970                  cn: {
33971                      cls:'x-tree-hd-text',
33972                      html: c.header
33973                  },
33974                  style:'width:'+(c.width-this.borderWidth)+'px;'
33975              }));
33976         }
33977         this.headers.createChild({cls:'x-clear'});
33978         // prevent floats from wrapping when clipped
33979         this.headers.setWidth(totalWidth);
33980         //this.innerCt.setWidth(totalWidth);
33981         this.innerCt.setStyle({ overflow: 'auto' });
33982         this.onResize(this.width, this.height);
33983              
33984         
33985     },
33986     onResize : function(w,h)
33987     {
33988         this.height = h;
33989         this.width = w;
33990         // resize cols..
33991         this.innerCt.setWidth(this.width);
33992         this.innerCt.setHeight(this.height-20);
33993         
33994         // headers...
33995         var cols = this.columns, c;
33996         var totalWidth = 0;
33997         var expEl = false;
33998         var len = cols.length;
33999         for(var i = 0; i < len; i++){
34000             c = cols[i];
34001             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
34002                 // it's the expander..
34003                 expEl  = this.headEls[i];
34004                 continue;
34005             }
34006             totalWidth += c.width;
34007             
34008         }
34009         if (expEl) {
34010             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34011         }
34012         this.headers.setWidth(w-20);
34013
34014         
34015         
34016         
34017     }
34018 });
34019 /*
34020  * Based on:
34021  * Ext JS Library 1.1.1
34022  * Copyright(c) 2006-2007, Ext JS, LLC.
34023  *
34024  * Originally Released Under LGPL - original licence link has changed is not relivant.
34025  *
34026  * Fork - LGPL
34027  * <script type="text/javascript">
34028  */
34029  
34030 /**
34031  * @class Roo.menu.Menu
34032  * @extends Roo.util.Observable
34033  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34034  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34035  * @constructor
34036  * Creates a new Menu
34037  * @param {Object} config Configuration options
34038  */
34039 Roo.menu.Menu = function(config){
34040     Roo.apply(this, config);
34041     this.id = this.id || Roo.id();
34042     this.addEvents({
34043         /**
34044          * @event beforeshow
34045          * Fires before this menu is displayed
34046          * @param {Roo.menu.Menu} this
34047          */
34048         beforeshow : true,
34049         /**
34050          * @event beforehide
34051          * Fires before this menu is hidden
34052          * @param {Roo.menu.Menu} this
34053          */
34054         beforehide : true,
34055         /**
34056          * @event show
34057          * Fires after this menu is displayed
34058          * @param {Roo.menu.Menu} this
34059          */
34060         show : true,
34061         /**
34062          * @event hide
34063          * Fires after this menu is hidden
34064          * @param {Roo.menu.Menu} this
34065          */
34066         hide : true,
34067         /**
34068          * @event click
34069          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34070          * @param {Roo.menu.Menu} this
34071          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34072          * @param {Roo.EventObject} e
34073          */
34074         click : true,
34075         /**
34076          * @event mouseover
34077          * Fires when the mouse is hovering over this menu
34078          * @param {Roo.menu.Menu} this
34079          * @param {Roo.EventObject} e
34080          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34081          */
34082         mouseover : true,
34083         /**
34084          * @event mouseout
34085          * Fires when the mouse exits this menu
34086          * @param {Roo.menu.Menu} this
34087          * @param {Roo.EventObject} e
34088          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34089          */
34090         mouseout : true,
34091         /**
34092          * @event itemclick
34093          * Fires when a menu item contained in this menu is clicked
34094          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34095          * @param {Roo.EventObject} e
34096          */
34097         itemclick: true
34098     });
34099     if (this.registerMenu) {
34100         Roo.menu.MenuMgr.register(this);
34101     }
34102     
34103     var mis = this.items;
34104     this.items = new Roo.util.MixedCollection();
34105     if(mis){
34106         this.add.apply(this, mis);
34107     }
34108 };
34109
34110 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34111     /**
34112      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34113      */
34114     minWidth : 120,
34115     /**
34116      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34117      * for bottom-right shadow (defaults to "sides")
34118      */
34119     shadow : "sides",
34120     /**
34121      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34122      * this menu (defaults to "tl-tr?")
34123      */
34124     subMenuAlign : "tl-tr?",
34125     /**
34126      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34127      * relative to its element of origin (defaults to "tl-bl?")
34128      */
34129     defaultAlign : "tl-bl?",
34130     /**
34131      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34132      */
34133     allowOtherMenus : false,
34134     /**
34135      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34136      */
34137     registerMenu : true,
34138
34139     hidden:true,
34140
34141     // private
34142     render : function(){
34143         if(this.el){
34144             return;
34145         }
34146         var el = this.el = new Roo.Layer({
34147             cls: "x-menu",
34148             shadow:this.shadow,
34149             constrain: false,
34150             parentEl: this.parentEl || document.body,
34151             zindex:15000
34152         });
34153
34154         this.keyNav = new Roo.menu.MenuNav(this);
34155
34156         if(this.plain){
34157             el.addClass("x-menu-plain");
34158         }
34159         if(this.cls){
34160             el.addClass(this.cls);
34161         }
34162         // generic focus element
34163         this.focusEl = el.createChild({
34164             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34165         });
34166         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34167         ul.on("click", this.onClick, this);
34168         ul.on("mouseover", this.onMouseOver, this);
34169         ul.on("mouseout", this.onMouseOut, this);
34170         this.items.each(function(item){
34171             var li = document.createElement("li");
34172             li.className = "x-menu-list-item";
34173             ul.dom.appendChild(li);
34174             item.render(li, this);
34175         }, this);
34176         this.ul = ul;
34177         this.autoWidth();
34178     },
34179
34180     // private
34181     autoWidth : function(){
34182         var el = this.el, ul = this.ul;
34183         if(!el){
34184             return;
34185         }
34186         var w = this.width;
34187         if(w){
34188             el.setWidth(w);
34189         }else if(Roo.isIE){
34190             el.setWidth(this.minWidth);
34191             var t = el.dom.offsetWidth; // force recalc
34192             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34193         }
34194     },
34195
34196     // private
34197     delayAutoWidth : function(){
34198         if(this.rendered){
34199             if(!this.awTask){
34200                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34201             }
34202             this.awTask.delay(20);
34203         }
34204     },
34205
34206     // private
34207     findTargetItem : function(e){
34208         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34209         if(t && t.menuItemId){
34210             return this.items.get(t.menuItemId);
34211         }
34212     },
34213
34214     // private
34215     onClick : function(e){
34216         var t;
34217         if(t = this.findTargetItem(e)){
34218             t.onClick(e);
34219             this.fireEvent("click", this, t, e);
34220         }
34221     },
34222
34223     // private
34224     setActiveItem : function(item, autoExpand){
34225         if(item != this.activeItem){
34226             if(this.activeItem){
34227                 this.activeItem.deactivate();
34228             }
34229             this.activeItem = item;
34230             item.activate(autoExpand);
34231         }else if(autoExpand){
34232             item.expandMenu();
34233         }
34234     },
34235
34236     // private
34237     tryActivate : function(start, step){
34238         var items = this.items;
34239         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34240             var item = items.get(i);
34241             if(!item.disabled && item.canActivate){
34242                 this.setActiveItem(item, false);
34243                 return item;
34244             }
34245         }
34246         return false;
34247     },
34248
34249     // private
34250     onMouseOver : function(e){
34251         var t;
34252         if(t = this.findTargetItem(e)){
34253             if(t.canActivate && !t.disabled){
34254                 this.setActiveItem(t, true);
34255             }
34256         }
34257         this.fireEvent("mouseover", this, e, t);
34258     },
34259
34260     // private
34261     onMouseOut : function(e){
34262         var t;
34263         if(t = this.findTargetItem(e)){
34264             if(t == this.activeItem && t.shouldDeactivate(e)){
34265                 this.activeItem.deactivate();
34266                 delete this.activeItem;
34267             }
34268         }
34269         this.fireEvent("mouseout", this, e, t);
34270     },
34271
34272     /**
34273      * Read-only.  Returns true if the menu is currently displayed, else false.
34274      * @type Boolean
34275      */
34276     isVisible : function(){
34277         return this.el && !this.hidden;
34278     },
34279
34280     /**
34281      * Displays this menu relative to another element
34282      * @param {String/HTMLElement/Roo.Element} element The element to align to
34283      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34284      * the element (defaults to this.defaultAlign)
34285      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34286      */
34287     show : function(el, pos, parentMenu){
34288         this.parentMenu = parentMenu;
34289         if(!this.el){
34290             this.render();
34291         }
34292         this.fireEvent("beforeshow", this);
34293         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34294     },
34295
34296     /**
34297      * Displays this menu at a specific xy position
34298      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34299      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34300      */
34301     showAt : function(xy, parentMenu, /* private: */_e){
34302         this.parentMenu = parentMenu;
34303         if(!this.el){
34304             this.render();
34305         }
34306         if(_e !== false){
34307             this.fireEvent("beforeshow", this);
34308             xy = this.el.adjustForConstraints(xy);
34309         }
34310         this.el.setXY(xy);
34311         this.el.show();
34312         this.hidden = false;
34313         this.focus();
34314         this.fireEvent("show", this);
34315     },
34316
34317     focus : function(){
34318         if(!this.hidden){
34319             this.doFocus.defer(50, this);
34320         }
34321     },
34322
34323     doFocus : function(){
34324         if(!this.hidden){
34325             this.focusEl.focus();
34326         }
34327     },
34328
34329     /**
34330      * Hides this menu and optionally all parent menus
34331      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34332      */
34333     hide : function(deep){
34334         if(this.el && this.isVisible()){
34335             this.fireEvent("beforehide", this);
34336             if(this.activeItem){
34337                 this.activeItem.deactivate();
34338                 this.activeItem = null;
34339             }
34340             this.el.hide();
34341             this.hidden = true;
34342             this.fireEvent("hide", this);
34343         }
34344         if(deep === true && this.parentMenu){
34345             this.parentMenu.hide(true);
34346         }
34347     },
34348
34349     /**
34350      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34351      * Any of the following are valid:
34352      * <ul>
34353      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34354      * <li>An HTMLElement object which will be converted to a menu item</li>
34355      * <li>A menu item config object that will be created as a new menu item</li>
34356      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34357      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34358      * </ul>
34359      * Usage:
34360      * <pre><code>
34361 // Create the menu
34362 var menu = new Roo.menu.Menu();
34363
34364 // Create a menu item to add by reference
34365 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34366
34367 // Add a bunch of items at once using different methods.
34368 // Only the last item added will be returned.
34369 var item = menu.add(
34370     menuItem,                // add existing item by ref
34371     'Dynamic Item',          // new TextItem
34372     '-',                     // new separator
34373     { text: 'Config Item' }  // new item by config
34374 );
34375 </code></pre>
34376      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34377      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34378      */
34379     add : function(){
34380         var a = arguments, l = a.length, item;
34381         for(var i = 0; i < l; i++){
34382             var el = a[i];
34383             if ((typeof(el) == "object") && el.xtype && el.xns) {
34384                 el = Roo.factory(el, Roo.menu);
34385             }
34386             
34387             if(el.render){ // some kind of Item
34388                 item = this.addItem(el);
34389             }else if(typeof el == "string"){ // string
34390                 if(el == "separator" || el == "-"){
34391                     item = this.addSeparator();
34392                 }else{
34393                     item = this.addText(el);
34394                 }
34395             }else if(el.tagName || el.el){ // element
34396                 item = this.addElement(el);
34397             }else if(typeof el == "object"){ // must be menu item config?
34398                 item = this.addMenuItem(el);
34399             }
34400         }
34401         return item;
34402     },
34403
34404     /**
34405      * Returns this menu's underlying {@link Roo.Element} object
34406      * @return {Roo.Element} The element
34407      */
34408     getEl : function(){
34409         if(!this.el){
34410             this.render();
34411         }
34412         return this.el;
34413     },
34414
34415     /**
34416      * Adds a separator bar to the menu
34417      * @return {Roo.menu.Item} The menu item that was added
34418      */
34419     addSeparator : function(){
34420         return this.addItem(new Roo.menu.Separator());
34421     },
34422
34423     /**
34424      * Adds an {@link Roo.Element} object to the menu
34425      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34426      * @return {Roo.menu.Item} The menu item that was added
34427      */
34428     addElement : function(el){
34429         return this.addItem(new Roo.menu.BaseItem(el));
34430     },
34431
34432     /**
34433      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34434      * @param {Roo.menu.Item} item The menu item to add
34435      * @return {Roo.menu.Item} The menu item that was added
34436      */
34437     addItem : function(item){
34438         this.items.add(item);
34439         if(this.ul){
34440             var li = document.createElement("li");
34441             li.className = "x-menu-list-item";
34442             this.ul.dom.appendChild(li);
34443             item.render(li, this);
34444             this.delayAutoWidth();
34445         }
34446         return item;
34447     },
34448
34449     /**
34450      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34451      * @param {Object} config A MenuItem config object
34452      * @return {Roo.menu.Item} The menu item that was added
34453      */
34454     addMenuItem : function(config){
34455         if(!(config instanceof Roo.menu.Item)){
34456             if(typeof config.checked == "boolean"){ // must be check menu item config?
34457                 config = new Roo.menu.CheckItem(config);
34458             }else{
34459                 config = new Roo.menu.Item(config);
34460             }
34461         }
34462         return this.addItem(config);
34463     },
34464
34465     /**
34466      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34467      * @param {String} text The text to display in the menu item
34468      * @return {Roo.menu.Item} The menu item that was added
34469      */
34470     addText : function(text){
34471         return this.addItem(new Roo.menu.TextItem({ text : text }));
34472     },
34473
34474     /**
34475      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34476      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34477      * @param {Roo.menu.Item} item The menu item to add
34478      * @return {Roo.menu.Item} The menu item that was added
34479      */
34480     insert : function(index, item){
34481         this.items.insert(index, item);
34482         if(this.ul){
34483             var li = document.createElement("li");
34484             li.className = "x-menu-list-item";
34485             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34486             item.render(li, this);
34487             this.delayAutoWidth();
34488         }
34489         return item;
34490     },
34491
34492     /**
34493      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34494      * @param {Roo.menu.Item} item The menu item to remove
34495      */
34496     remove : function(item){
34497         this.items.removeKey(item.id);
34498         item.destroy();
34499     },
34500
34501     /**
34502      * Removes and destroys all items in the menu
34503      */
34504     removeAll : function(){
34505         var f;
34506         while(f = this.items.first()){
34507             this.remove(f);
34508         }
34509     }
34510 });
34511
34512 // MenuNav is a private utility class used internally by the Menu
34513 Roo.menu.MenuNav = function(menu){
34514     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34515     this.scope = this.menu = menu;
34516 };
34517
34518 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34519     doRelay : function(e, h){
34520         var k = e.getKey();
34521         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34522             this.menu.tryActivate(0, 1);
34523             return false;
34524         }
34525         return h.call(this.scope || this, e, this.menu);
34526     },
34527
34528     up : function(e, m){
34529         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34530             m.tryActivate(m.items.length-1, -1);
34531         }
34532     },
34533
34534     down : function(e, m){
34535         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34536             m.tryActivate(0, 1);
34537         }
34538     },
34539
34540     right : function(e, m){
34541         if(m.activeItem){
34542             m.activeItem.expandMenu(true);
34543         }
34544     },
34545
34546     left : function(e, m){
34547         m.hide();
34548         if(m.parentMenu && m.parentMenu.activeItem){
34549             m.parentMenu.activeItem.activate();
34550         }
34551     },
34552
34553     enter : function(e, m){
34554         if(m.activeItem){
34555             e.stopPropagation();
34556             m.activeItem.onClick(e);
34557             m.fireEvent("click", this, m.activeItem);
34558             return true;
34559         }
34560     }
34561 });/*
34562  * Based on:
34563  * Ext JS Library 1.1.1
34564  * Copyright(c) 2006-2007, Ext JS, LLC.
34565  *
34566  * Originally Released Under LGPL - original licence link has changed is not relivant.
34567  *
34568  * Fork - LGPL
34569  * <script type="text/javascript">
34570  */
34571  
34572 /**
34573  * @class Roo.menu.MenuMgr
34574  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34575  * @singleton
34576  */
34577 Roo.menu.MenuMgr = function(){
34578    var menus, active, groups = {}, attached = false, lastShow = new Date();
34579
34580    // private - called when first menu is created
34581    function init(){
34582        menus = {};
34583        active = new Roo.util.MixedCollection();
34584        Roo.get(document).addKeyListener(27, function(){
34585            if(active.length > 0){
34586                hideAll();
34587            }
34588        });
34589    }
34590
34591    // private
34592    function hideAll(){
34593        if(active && active.length > 0){
34594            var c = active.clone();
34595            c.each(function(m){
34596                m.hide();
34597            });
34598        }
34599    }
34600
34601    // private
34602    function onHide(m){
34603        active.remove(m);
34604        if(active.length < 1){
34605            Roo.get(document).un("mousedown", onMouseDown);
34606            attached = false;
34607        }
34608    }
34609
34610    // private
34611    function onShow(m){
34612        var last = active.last();
34613        lastShow = new Date();
34614        active.add(m);
34615        if(!attached){
34616            Roo.get(document).on("mousedown", onMouseDown);
34617            attached = true;
34618        }
34619        if(m.parentMenu){
34620           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34621           m.parentMenu.activeChild = m;
34622        }else if(last && last.isVisible()){
34623           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34624        }
34625    }
34626
34627    // private
34628    function onBeforeHide(m){
34629        if(m.activeChild){
34630            m.activeChild.hide();
34631        }
34632        if(m.autoHideTimer){
34633            clearTimeout(m.autoHideTimer);
34634            delete m.autoHideTimer;
34635        }
34636    }
34637
34638    // private
34639    function onBeforeShow(m){
34640        var pm = m.parentMenu;
34641        if(!pm && !m.allowOtherMenus){
34642            hideAll();
34643        }else if(pm && pm.activeChild && active != m){
34644            pm.activeChild.hide();
34645        }
34646    }
34647
34648    // private
34649    function onMouseDown(e){
34650        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34651            hideAll();
34652        }
34653    }
34654
34655    // private
34656    function onBeforeCheck(mi, state){
34657        if(state){
34658            var g = groups[mi.group];
34659            for(var i = 0, l = g.length; i < l; i++){
34660                if(g[i] != mi){
34661                    g[i].setChecked(false);
34662                }
34663            }
34664        }
34665    }
34666
34667    return {
34668
34669        /**
34670         * Hides all menus that are currently visible
34671         */
34672        hideAll : function(){
34673             hideAll();  
34674        },
34675
34676        // private
34677        register : function(menu){
34678            if(!menus){
34679                init();
34680            }
34681            menus[menu.id] = menu;
34682            menu.on("beforehide", onBeforeHide);
34683            menu.on("hide", onHide);
34684            menu.on("beforeshow", onBeforeShow);
34685            menu.on("show", onShow);
34686            var g = menu.group;
34687            if(g && menu.events["checkchange"]){
34688                if(!groups[g]){
34689                    groups[g] = [];
34690                }
34691                groups[g].push(menu);
34692                menu.on("checkchange", onCheck);
34693            }
34694        },
34695
34696         /**
34697          * Returns a {@link Roo.menu.Menu} object
34698          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34699          * be used to generate and return a new Menu instance.
34700          */
34701        get : function(menu){
34702            if(typeof menu == "string"){ // menu id
34703                return menus[menu];
34704            }else if(menu.events){  // menu instance
34705                return menu;
34706            }else if(typeof menu.length == 'number'){ // array of menu items?
34707                return new Roo.menu.Menu({items:menu});
34708            }else{ // otherwise, must be a config
34709                return new Roo.menu.Menu(menu);
34710            }
34711        },
34712
34713        // private
34714        unregister : function(menu){
34715            delete menus[menu.id];
34716            menu.un("beforehide", onBeforeHide);
34717            menu.un("hide", onHide);
34718            menu.un("beforeshow", onBeforeShow);
34719            menu.un("show", onShow);
34720            var g = menu.group;
34721            if(g && menu.events["checkchange"]){
34722                groups[g].remove(menu);
34723                menu.un("checkchange", onCheck);
34724            }
34725        },
34726
34727        // private
34728        registerCheckable : function(menuItem){
34729            var g = menuItem.group;
34730            if(g){
34731                if(!groups[g]){
34732                    groups[g] = [];
34733                }
34734                groups[g].push(menuItem);
34735                menuItem.on("beforecheckchange", onBeforeCheck);
34736            }
34737        },
34738
34739        // private
34740        unregisterCheckable : function(menuItem){
34741            var g = menuItem.group;
34742            if(g){
34743                groups[g].remove(menuItem);
34744                menuItem.un("beforecheckchange", onBeforeCheck);
34745            }
34746        }
34747    };
34748 }();/*
34749  * Based on:
34750  * Ext JS Library 1.1.1
34751  * Copyright(c) 2006-2007, Ext JS, LLC.
34752  *
34753  * Originally Released Under LGPL - original licence link has changed is not relivant.
34754  *
34755  * Fork - LGPL
34756  * <script type="text/javascript">
34757  */
34758  
34759
34760 /**
34761  * @class Roo.menu.BaseItem
34762  * @extends Roo.Component
34763  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34764  * management and base configuration options shared by all menu components.
34765  * @constructor
34766  * Creates a new BaseItem
34767  * @param {Object} config Configuration options
34768  */
34769 Roo.menu.BaseItem = function(config){
34770     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34771
34772     this.addEvents({
34773         /**
34774          * @event click
34775          * Fires when this item is clicked
34776          * @param {Roo.menu.BaseItem} this
34777          * @param {Roo.EventObject} e
34778          */
34779         click: true,
34780         /**
34781          * @event activate
34782          * Fires when this item is activated
34783          * @param {Roo.menu.BaseItem} this
34784          */
34785         activate : true,
34786         /**
34787          * @event deactivate
34788          * Fires when this item is deactivated
34789          * @param {Roo.menu.BaseItem} this
34790          */
34791         deactivate : true
34792     });
34793
34794     if(this.handler){
34795         this.on("click", this.handler, this.scope, true);
34796     }
34797 };
34798
34799 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34800     /**
34801      * @cfg {Function} handler
34802      * A function that will handle the click event of this menu item (defaults to undefined)
34803      */
34804     /**
34805      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34806      */
34807     canActivate : false,
34808     /**
34809      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34810      */
34811     activeClass : "x-menu-item-active",
34812     /**
34813      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34814      */
34815     hideOnClick : true,
34816     /**
34817      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34818      */
34819     hideDelay : 100,
34820
34821     // private
34822     ctype: "Roo.menu.BaseItem",
34823
34824     // private
34825     actionMode : "container",
34826
34827     // private
34828     render : function(container, parentMenu){
34829         this.parentMenu = parentMenu;
34830         Roo.menu.BaseItem.superclass.render.call(this, container);
34831         this.container.menuItemId = this.id;
34832     },
34833
34834     // private
34835     onRender : function(container, position){
34836         this.el = Roo.get(this.el);
34837         container.dom.appendChild(this.el.dom);
34838     },
34839
34840     // private
34841     onClick : function(e){
34842         if(!this.disabled && this.fireEvent("click", this, e) !== false
34843                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34844             this.handleClick(e);
34845         }else{
34846             e.stopEvent();
34847         }
34848     },
34849
34850     // private
34851     activate : function(){
34852         if(this.disabled){
34853             return false;
34854         }
34855         var li = this.container;
34856         li.addClass(this.activeClass);
34857         this.region = li.getRegion().adjust(2, 2, -2, -2);
34858         this.fireEvent("activate", this);
34859         return true;
34860     },
34861
34862     // private
34863     deactivate : function(){
34864         this.container.removeClass(this.activeClass);
34865         this.fireEvent("deactivate", this);
34866     },
34867
34868     // private
34869     shouldDeactivate : function(e){
34870         return !this.region || !this.region.contains(e.getPoint());
34871     },
34872
34873     // private
34874     handleClick : function(e){
34875         if(this.hideOnClick){
34876             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34877         }
34878     },
34879
34880     // private
34881     expandMenu : function(autoActivate){
34882         // do nothing
34883     },
34884
34885     // private
34886     hideMenu : function(){
34887         // do nothing
34888     }
34889 });/*
34890  * Based on:
34891  * Ext JS Library 1.1.1
34892  * Copyright(c) 2006-2007, Ext JS, LLC.
34893  *
34894  * Originally Released Under LGPL - original licence link has changed is not relivant.
34895  *
34896  * Fork - LGPL
34897  * <script type="text/javascript">
34898  */
34899  
34900 /**
34901  * @class Roo.menu.Adapter
34902  * @extends Roo.menu.BaseItem
34903  * 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.
34904  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34905  * @constructor
34906  * Creates a new Adapter
34907  * @param {Object} config Configuration options
34908  */
34909 Roo.menu.Adapter = function(component, config){
34910     Roo.menu.Adapter.superclass.constructor.call(this, config);
34911     this.component = component;
34912 };
34913 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34914     // private
34915     canActivate : true,
34916
34917     // private
34918     onRender : function(container, position){
34919         this.component.render(container);
34920         this.el = this.component.getEl();
34921     },
34922
34923     // private
34924     activate : function(){
34925         if(this.disabled){
34926             return false;
34927         }
34928         this.component.focus();
34929         this.fireEvent("activate", this);
34930         return true;
34931     },
34932
34933     // private
34934     deactivate : function(){
34935         this.fireEvent("deactivate", this);
34936     },
34937
34938     // private
34939     disable : function(){
34940         this.component.disable();
34941         Roo.menu.Adapter.superclass.disable.call(this);
34942     },
34943
34944     // private
34945     enable : function(){
34946         this.component.enable();
34947         Roo.menu.Adapter.superclass.enable.call(this);
34948     }
34949 });/*
34950  * Based on:
34951  * Ext JS Library 1.1.1
34952  * Copyright(c) 2006-2007, Ext JS, LLC.
34953  *
34954  * Originally Released Under LGPL - original licence link has changed is not relivant.
34955  *
34956  * Fork - LGPL
34957  * <script type="text/javascript">
34958  */
34959
34960 /**
34961  * @class Roo.menu.TextItem
34962  * @extends Roo.menu.BaseItem
34963  * Adds a static text string to a menu, usually used as either a heading or group separator.
34964  * Note: old style constructor with text is still supported.
34965  * 
34966  * @constructor
34967  * Creates a new TextItem
34968  * @param {Object} cfg Configuration
34969  */
34970 Roo.menu.TextItem = function(cfg){
34971     if (typeof(cfg) == 'string') {
34972         this.text = cfg;
34973     } else {
34974         Roo.apply(this,cfg);
34975     }
34976     
34977     Roo.menu.TextItem.superclass.constructor.call(this);
34978 };
34979
34980 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34981     /**
34982      * @cfg {Boolean} text Text to show on item.
34983      */
34984     text : '',
34985     
34986     /**
34987      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34988      */
34989     hideOnClick : false,
34990     /**
34991      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34992      */
34993     itemCls : "x-menu-text",
34994
34995     // private
34996     onRender : function(){
34997         var s = document.createElement("span");
34998         s.className = this.itemCls;
34999         s.innerHTML = this.text;
35000         this.el = s;
35001         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
35002     }
35003 });/*
35004  * Based on:
35005  * Ext JS Library 1.1.1
35006  * Copyright(c) 2006-2007, Ext JS, LLC.
35007  *
35008  * Originally Released Under LGPL - original licence link has changed is not relivant.
35009  *
35010  * Fork - LGPL
35011  * <script type="text/javascript">
35012  */
35013
35014 /**
35015  * @class Roo.menu.Separator
35016  * @extends Roo.menu.BaseItem
35017  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35018  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35019  * @constructor
35020  * @param {Object} config Configuration options
35021  */
35022 Roo.menu.Separator = function(config){
35023     Roo.menu.Separator.superclass.constructor.call(this, config);
35024 };
35025
35026 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35027     /**
35028      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35029      */
35030     itemCls : "x-menu-sep",
35031     /**
35032      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35033      */
35034     hideOnClick : false,
35035
35036     // private
35037     onRender : function(li){
35038         var s = document.createElement("span");
35039         s.className = this.itemCls;
35040         s.innerHTML = "&#160;";
35041         this.el = s;
35042         li.addClass("x-menu-sep-li");
35043         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35044     }
35045 });/*
35046  * Based on:
35047  * Ext JS Library 1.1.1
35048  * Copyright(c) 2006-2007, Ext JS, LLC.
35049  *
35050  * Originally Released Under LGPL - original licence link has changed is not relivant.
35051  *
35052  * Fork - LGPL
35053  * <script type="text/javascript">
35054  */
35055 /**
35056  * @class Roo.menu.Item
35057  * @extends Roo.menu.BaseItem
35058  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35059  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35060  * activation and click handling.
35061  * @constructor
35062  * Creates a new Item
35063  * @param {Object} config Configuration options
35064  */
35065 Roo.menu.Item = function(config){
35066     Roo.menu.Item.superclass.constructor.call(this, config);
35067     if(this.menu){
35068         this.menu = Roo.menu.MenuMgr.get(this.menu);
35069     }
35070 };
35071 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35072     
35073     /**
35074      * @cfg {String} text
35075      * The text to show on the menu item.
35076      */
35077     text: '',
35078      /**
35079      * @cfg {String} HTML to render in menu
35080      * The text to show on the menu item (HTML version).
35081      */
35082     html: '',
35083     /**
35084      * @cfg {String} icon
35085      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35086      */
35087     icon: undefined,
35088     /**
35089      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35090      */
35091     itemCls : "x-menu-item",
35092     /**
35093      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35094      */
35095     canActivate : true,
35096     /**
35097      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35098      */
35099     showDelay: 200,
35100     // doc'd in BaseItem
35101     hideDelay: 200,
35102
35103     // private
35104     ctype: "Roo.menu.Item",
35105     
35106     // private
35107     onRender : function(container, position){
35108         var el = document.createElement("a");
35109         el.hideFocus = true;
35110         el.unselectable = "on";
35111         el.href = this.href || "#";
35112         if(this.hrefTarget){
35113             el.target = this.hrefTarget;
35114         }
35115         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35116         
35117         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35118         
35119         el.innerHTML = String.format(
35120                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35121                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35122         this.el = el;
35123         Roo.menu.Item.superclass.onRender.call(this, container, position);
35124     },
35125
35126     /**
35127      * Sets the text to display in this menu item
35128      * @param {String} text The text to display
35129      * @param {Boolean} isHTML true to indicate text is pure html.
35130      */
35131     setText : function(text, isHTML){
35132         if (isHTML) {
35133             this.html = text;
35134         } else {
35135             this.text = text;
35136             this.html = '';
35137         }
35138         if(this.rendered){
35139             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35140      
35141             this.el.update(String.format(
35142                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35143                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35144             this.parentMenu.autoWidth();
35145         }
35146     },
35147
35148     // private
35149     handleClick : function(e){
35150         if(!this.href){ // if no link defined, stop the event automatically
35151             e.stopEvent();
35152         }
35153         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35154     },
35155
35156     // private
35157     activate : function(autoExpand){
35158         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35159             this.focus();
35160             if(autoExpand){
35161                 this.expandMenu();
35162             }
35163         }
35164         return true;
35165     },
35166
35167     // private
35168     shouldDeactivate : function(e){
35169         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35170             if(this.menu && this.menu.isVisible()){
35171                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35172             }
35173             return true;
35174         }
35175         return false;
35176     },
35177
35178     // private
35179     deactivate : function(){
35180         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35181         this.hideMenu();
35182     },
35183
35184     // private
35185     expandMenu : function(autoActivate){
35186         if(!this.disabled && this.menu){
35187             clearTimeout(this.hideTimer);
35188             delete this.hideTimer;
35189             if(!this.menu.isVisible() && !this.showTimer){
35190                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35191             }else if (this.menu.isVisible() && autoActivate){
35192                 this.menu.tryActivate(0, 1);
35193             }
35194         }
35195     },
35196
35197     // private
35198     deferExpand : function(autoActivate){
35199         delete this.showTimer;
35200         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35201         if(autoActivate){
35202             this.menu.tryActivate(0, 1);
35203         }
35204     },
35205
35206     // private
35207     hideMenu : function(){
35208         clearTimeout(this.showTimer);
35209         delete this.showTimer;
35210         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35211             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35212         }
35213     },
35214
35215     // private
35216     deferHide : function(){
35217         delete this.hideTimer;
35218         this.menu.hide();
35219     }
35220 });/*
35221  * Based on:
35222  * Ext JS Library 1.1.1
35223  * Copyright(c) 2006-2007, Ext JS, LLC.
35224  *
35225  * Originally Released Under LGPL - original licence link has changed is not relivant.
35226  *
35227  * Fork - LGPL
35228  * <script type="text/javascript">
35229  */
35230  
35231 /**
35232  * @class Roo.menu.CheckItem
35233  * @extends Roo.menu.Item
35234  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35235  * @constructor
35236  * Creates a new CheckItem
35237  * @param {Object} config Configuration options
35238  */
35239 Roo.menu.CheckItem = function(config){
35240     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35241     this.addEvents({
35242         /**
35243          * @event beforecheckchange
35244          * Fires before the checked value is set, providing an opportunity to cancel if needed
35245          * @param {Roo.menu.CheckItem} this
35246          * @param {Boolean} checked The new checked value that will be set
35247          */
35248         "beforecheckchange" : true,
35249         /**
35250          * @event checkchange
35251          * Fires after the checked value has been set
35252          * @param {Roo.menu.CheckItem} this
35253          * @param {Boolean} checked The checked value that was set
35254          */
35255         "checkchange" : true
35256     });
35257     if(this.checkHandler){
35258         this.on('checkchange', this.checkHandler, this.scope);
35259     }
35260 };
35261 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35262     /**
35263      * @cfg {String} group
35264      * All check items with the same group name will automatically be grouped into a single-select
35265      * radio button group (defaults to '')
35266      */
35267     /**
35268      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35269      */
35270     itemCls : "x-menu-item x-menu-check-item",
35271     /**
35272      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35273      */
35274     groupClass : "x-menu-group-item",
35275
35276     /**
35277      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35278      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35279      * initialized with checked = true will be rendered as checked.
35280      */
35281     checked: false,
35282
35283     // private
35284     ctype: "Roo.menu.CheckItem",
35285
35286     // private
35287     onRender : function(c){
35288         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35289         if(this.group){
35290             this.el.addClass(this.groupClass);
35291         }
35292         Roo.menu.MenuMgr.registerCheckable(this);
35293         if(this.checked){
35294             this.checked = false;
35295             this.setChecked(true, true);
35296         }
35297     },
35298
35299     // private
35300     destroy : function(){
35301         if(this.rendered){
35302             Roo.menu.MenuMgr.unregisterCheckable(this);
35303         }
35304         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35305     },
35306
35307     /**
35308      * Set the checked state of this item
35309      * @param {Boolean} checked The new checked value
35310      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35311      */
35312     setChecked : function(state, suppressEvent){
35313         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35314             if(this.container){
35315                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35316             }
35317             this.checked = state;
35318             if(suppressEvent !== true){
35319                 this.fireEvent("checkchange", this, state);
35320             }
35321         }
35322     },
35323
35324     // private
35325     handleClick : function(e){
35326        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35327            this.setChecked(!this.checked);
35328        }
35329        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35330     }
35331 });/*
35332  * Based on:
35333  * Ext JS Library 1.1.1
35334  * Copyright(c) 2006-2007, Ext JS, LLC.
35335  *
35336  * Originally Released Under LGPL - original licence link has changed is not relivant.
35337  *
35338  * Fork - LGPL
35339  * <script type="text/javascript">
35340  */
35341  
35342 /**
35343  * @class Roo.menu.DateItem
35344  * @extends Roo.menu.Adapter
35345  * A menu item that wraps the {@link Roo.DatPicker} component.
35346  * @constructor
35347  * Creates a new DateItem
35348  * @param {Object} config Configuration options
35349  */
35350 Roo.menu.DateItem = function(config){
35351     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35352     /** The Roo.DatePicker object @type Roo.DatePicker */
35353     this.picker = this.component;
35354     this.addEvents({select: true});
35355     
35356     this.picker.on("render", function(picker){
35357         picker.getEl().swallowEvent("click");
35358         picker.container.addClass("x-menu-date-item");
35359     });
35360
35361     this.picker.on("select", this.onSelect, this);
35362 };
35363
35364 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35365     // private
35366     onSelect : function(picker, date){
35367         this.fireEvent("select", this, date, picker);
35368         Roo.menu.DateItem.superclass.handleClick.call(this);
35369     }
35370 });/*
35371  * Based on:
35372  * Ext JS Library 1.1.1
35373  * Copyright(c) 2006-2007, Ext JS, LLC.
35374  *
35375  * Originally Released Under LGPL - original licence link has changed is not relivant.
35376  *
35377  * Fork - LGPL
35378  * <script type="text/javascript">
35379  */
35380  
35381 /**
35382  * @class Roo.menu.ColorItem
35383  * @extends Roo.menu.Adapter
35384  * A menu item that wraps the {@link Roo.ColorPalette} component.
35385  * @constructor
35386  * Creates a new ColorItem
35387  * @param {Object} config Configuration options
35388  */
35389 Roo.menu.ColorItem = function(config){
35390     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35391     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35392     this.palette = this.component;
35393     this.relayEvents(this.palette, ["select"]);
35394     if(this.selectHandler){
35395         this.on('select', this.selectHandler, this.scope);
35396     }
35397 };
35398 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35399  * Based on:
35400  * Ext JS Library 1.1.1
35401  * Copyright(c) 2006-2007, Ext JS, LLC.
35402  *
35403  * Originally Released Under LGPL - original licence link has changed is not relivant.
35404  *
35405  * Fork - LGPL
35406  * <script type="text/javascript">
35407  */
35408  
35409
35410 /**
35411  * @class Roo.menu.DateMenu
35412  * @extends Roo.menu.Menu
35413  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35414  * @constructor
35415  * Creates a new DateMenu
35416  * @param {Object} config Configuration options
35417  */
35418 Roo.menu.DateMenu = function(config){
35419     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35420     this.plain = true;
35421     var di = new Roo.menu.DateItem(config);
35422     this.add(di);
35423     /**
35424      * The {@link Roo.DatePicker} instance for this DateMenu
35425      * @type DatePicker
35426      */
35427     this.picker = di.picker;
35428     /**
35429      * @event select
35430      * @param {DatePicker} picker
35431      * @param {Date} date
35432      */
35433     this.relayEvents(di, ["select"]);
35434
35435     this.on('beforeshow', function(){
35436         if(this.picker){
35437             this.picker.hideMonthPicker(true);
35438         }
35439     }, this);
35440 };
35441 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35442     cls:'x-date-menu'
35443 });/*
35444  * Based on:
35445  * Ext JS Library 1.1.1
35446  * Copyright(c) 2006-2007, Ext JS, LLC.
35447  *
35448  * Originally Released Under LGPL - original licence link has changed is not relivant.
35449  *
35450  * Fork - LGPL
35451  * <script type="text/javascript">
35452  */
35453  
35454
35455 /**
35456  * @class Roo.menu.ColorMenu
35457  * @extends Roo.menu.Menu
35458  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35459  * @constructor
35460  * Creates a new ColorMenu
35461  * @param {Object} config Configuration options
35462  */
35463 Roo.menu.ColorMenu = function(config){
35464     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35465     this.plain = true;
35466     var ci = new Roo.menu.ColorItem(config);
35467     this.add(ci);
35468     /**
35469      * The {@link Roo.ColorPalette} instance for this ColorMenu
35470      * @type ColorPalette
35471      */
35472     this.palette = ci.palette;
35473     /**
35474      * @event select
35475      * @param {ColorPalette} palette
35476      * @param {String} color
35477      */
35478     this.relayEvents(ci, ["select"]);
35479 };
35480 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35481  * Based on:
35482  * Ext JS Library 1.1.1
35483  * Copyright(c) 2006-2007, Ext JS, LLC.
35484  *
35485  * Originally Released Under LGPL - original licence link has changed is not relivant.
35486  *
35487  * Fork - LGPL
35488  * <script type="text/javascript">
35489  */
35490  
35491 /**
35492  * @class Roo.form.Field
35493  * @extends Roo.BoxComponent
35494  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35495  * @constructor
35496  * Creates a new Field
35497  * @param {Object} config Configuration options
35498  */
35499 Roo.form.Field = function(config){
35500     Roo.form.Field.superclass.constructor.call(this, config);
35501 };
35502
35503 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35504     /**
35505      * @cfg {String} fieldLabel Label to use when rendering a form.
35506      */
35507        /**
35508      * @cfg {String} qtip Mouse over tip
35509      */
35510      
35511     /**
35512      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35513      */
35514     invalidClass : "x-form-invalid",
35515     /**
35516      * @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")
35517      */
35518     invalidText : "The value in this field is invalid",
35519     /**
35520      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35521      */
35522     focusClass : "x-form-focus",
35523     /**
35524      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35525       automatic validation (defaults to "keyup").
35526      */
35527     validationEvent : "keyup",
35528     /**
35529      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35530      */
35531     validateOnBlur : true,
35532     /**
35533      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35534      */
35535     validationDelay : 250,
35536     /**
35537      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35538      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35539      */
35540     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35541     /**
35542      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35543      */
35544     fieldClass : "x-form-field",
35545     /**
35546      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35547      *<pre>
35548 Value         Description
35549 -----------   ----------------------------------------------------------------------
35550 qtip          Display a quick tip when the user hovers over the field
35551 title         Display a default browser title attribute popup
35552 under         Add a block div beneath the field containing the error text
35553 side          Add an error icon to the right of the field with a popup on hover
35554 [element id]  Add the error text directly to the innerHTML of the specified element
35555 </pre>
35556      */
35557     msgTarget : 'qtip',
35558     /**
35559      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35560      */
35561     msgFx : 'normal',
35562
35563     /**
35564      * @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.
35565      */
35566     readOnly : false,
35567
35568     /**
35569      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35570      */
35571     disabled : false,
35572
35573     /**
35574      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35575      */
35576     inputType : undefined,
35577     
35578     /**
35579      * @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).
35580          */
35581         tabIndex : undefined,
35582         
35583     // private
35584     isFormField : true,
35585
35586     // private
35587     hasFocus : false,
35588     /**
35589      * @property {Roo.Element} fieldEl
35590      * Element Containing the rendered Field (with label etc.)
35591      */
35592     /**
35593      * @cfg {Mixed} value A value to initialize this field with.
35594      */
35595     value : undefined,
35596
35597     /**
35598      * @cfg {String} name The field's HTML name attribute.
35599      */
35600     /**
35601      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35602      */
35603
35604         // private ??
35605         initComponent : function(){
35606         Roo.form.Field.superclass.initComponent.call(this);
35607         this.addEvents({
35608             /**
35609              * @event focus
35610              * Fires when this field receives input focus.
35611              * @param {Roo.form.Field} this
35612              */
35613             focus : true,
35614             /**
35615              * @event blur
35616              * Fires when this field loses input focus.
35617              * @param {Roo.form.Field} this
35618              */
35619             blur : true,
35620             /**
35621              * @event specialkey
35622              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35623              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35624              * @param {Roo.form.Field} this
35625              * @param {Roo.EventObject} e The event object
35626              */
35627             specialkey : true,
35628             /**
35629              * @event change
35630              * Fires just before the field blurs if the field value has changed.
35631              * @param {Roo.form.Field} this
35632              * @param {Mixed} newValue The new value
35633              * @param {Mixed} oldValue The original value
35634              */
35635             change : true,
35636             /**
35637              * @event invalid
35638              * Fires after the field has been marked as invalid.
35639              * @param {Roo.form.Field} this
35640              * @param {String} msg The validation message
35641              */
35642             invalid : true,
35643             /**
35644              * @event valid
35645              * Fires after the field has been validated with no errors.
35646              * @param {Roo.form.Field} this
35647              */
35648             valid : true,
35649              /**
35650              * @event keyup
35651              * Fires after the key up
35652              * @param {Roo.form.Field} this
35653              * @param {Roo.EventObject}  e The event Object
35654              */
35655             keyup : true
35656         });
35657     },
35658
35659     /**
35660      * Returns the name attribute of the field if available
35661      * @return {String} name The field name
35662      */
35663     getName: function(){
35664          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35665     },
35666
35667     // private
35668     onRender : function(ct, position){
35669         Roo.form.Field.superclass.onRender.call(this, ct, position);
35670         if(!this.el){
35671             var cfg = this.getAutoCreate();
35672             if(!cfg.name){
35673                 cfg.name = this.name || this.id;
35674             }
35675             if(this.inputType){
35676                 cfg.type = this.inputType;
35677             }
35678             this.el = ct.createChild(cfg, position);
35679         }
35680         var type = this.el.dom.type;
35681         if(type){
35682             if(type == 'password'){
35683                 type = 'text';
35684             }
35685             this.el.addClass('x-form-'+type);
35686         }
35687         if(this.readOnly){
35688             this.el.dom.readOnly = true;
35689         }
35690         if(this.tabIndex !== undefined){
35691             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35692         }
35693
35694         this.el.addClass([this.fieldClass, this.cls]);
35695         this.initValue();
35696     },
35697
35698     /**
35699      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35700      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35701      * @return {Roo.form.Field} this
35702      */
35703     applyTo : function(target){
35704         this.allowDomMove = false;
35705         this.el = Roo.get(target);
35706         this.render(this.el.dom.parentNode);
35707         return this;
35708     },
35709
35710     // private
35711     initValue : function(){
35712         if(this.value !== undefined){
35713             this.setValue(this.value);
35714         }else if(this.el.dom.value.length > 0){
35715             this.setValue(this.el.dom.value);
35716         }
35717     },
35718
35719     /**
35720      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35721      */
35722     isDirty : function() {
35723         if(this.disabled) {
35724             return false;
35725         }
35726         return String(this.getValue()) !== String(this.originalValue);
35727     },
35728
35729     // private
35730     afterRender : function(){
35731         Roo.form.Field.superclass.afterRender.call(this);
35732         this.initEvents();
35733     },
35734
35735     // private
35736     fireKey : function(e){
35737         //Roo.log('field ' + e.getKey());
35738         if(e.isNavKeyPress()){
35739             this.fireEvent("specialkey", this, e);
35740         }
35741     },
35742
35743     /**
35744      * Resets the current field value to the originally loaded value and clears any validation messages
35745      */
35746     reset : function(){
35747         this.setValue(this.originalValue);
35748         this.clearInvalid();
35749     },
35750
35751     // private
35752     initEvents : function(){
35753         // safari killled keypress - so keydown is now used..
35754         this.el.on("keydown" , this.fireKey,  this);
35755         this.el.on("focus", this.onFocus,  this);
35756         this.el.on("blur", this.onBlur,  this);
35757         this.el.relayEvent('keyup', this);
35758
35759         // reference to original value for reset
35760         this.originalValue = this.getValue();
35761     },
35762
35763     // private
35764     onFocus : function(){
35765         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35766             this.el.addClass(this.focusClass);
35767         }
35768         if(!this.hasFocus){
35769             this.hasFocus = true;
35770             this.startValue = this.getValue();
35771             this.fireEvent("focus", this);
35772         }
35773     },
35774
35775     beforeBlur : Roo.emptyFn,
35776
35777     // private
35778     onBlur : function(){
35779         this.beforeBlur();
35780         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35781             this.el.removeClass(this.focusClass);
35782         }
35783         this.hasFocus = false;
35784         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35785             this.validate();
35786         }
35787         var v = this.getValue();
35788         if(String(v) !== String(this.startValue)){
35789             this.fireEvent('change', this, v, this.startValue);
35790         }
35791         this.fireEvent("blur", this);
35792     },
35793
35794     /**
35795      * Returns whether or not the field value is currently valid
35796      * @param {Boolean} preventMark True to disable marking the field invalid
35797      * @return {Boolean} True if the value is valid, else false
35798      */
35799     isValid : function(preventMark){
35800         if(this.disabled){
35801             return true;
35802         }
35803         var restore = this.preventMark;
35804         this.preventMark = preventMark === true;
35805         var v = this.validateValue(this.processValue(this.getRawValue()));
35806         this.preventMark = restore;
35807         return v;
35808     },
35809
35810     /**
35811      * Validates the field value
35812      * @return {Boolean} True if the value is valid, else false
35813      */
35814     validate : function(){
35815         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35816             this.clearInvalid();
35817             return true;
35818         }
35819         return false;
35820     },
35821
35822     processValue : function(value){
35823         return value;
35824     },
35825
35826     // private
35827     // Subclasses should provide the validation implementation by overriding this
35828     validateValue : function(value){
35829         return true;
35830     },
35831
35832     /**
35833      * Mark this field as invalid
35834      * @param {String} msg The validation message
35835      */
35836     markInvalid : function(msg){
35837         if(!this.rendered || this.preventMark){ // not rendered
35838             return;
35839         }
35840         this.el.addClass(this.invalidClass);
35841         msg = msg || this.invalidText;
35842         switch(this.msgTarget){
35843             case 'qtip':
35844                 this.el.dom.qtip = msg;
35845                 this.el.dom.qclass = 'x-form-invalid-tip';
35846                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35847                     Roo.QuickTips.enable();
35848                 }
35849                 break;
35850             case 'title':
35851                 this.el.dom.title = msg;
35852                 break;
35853             case 'under':
35854                 if(!this.errorEl){
35855                     var elp = this.el.findParent('.x-form-element', 5, true);
35856                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35857                     this.errorEl.setWidth(elp.getWidth(true)-20);
35858                 }
35859                 this.errorEl.update(msg);
35860                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35861                 break;
35862             case 'side':
35863                 if(!this.errorIcon){
35864                     var elp = this.el.findParent('.x-form-element', 5, true);
35865                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35866                 }
35867                 this.alignErrorIcon();
35868                 this.errorIcon.dom.qtip = msg;
35869                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35870                 this.errorIcon.show();
35871                 this.on('resize', this.alignErrorIcon, this);
35872                 break;
35873             default:
35874                 var t = Roo.getDom(this.msgTarget);
35875                 t.innerHTML = msg;
35876                 t.style.display = this.msgDisplay;
35877                 break;
35878         }
35879         this.fireEvent('invalid', this, msg);
35880     },
35881
35882     // private
35883     alignErrorIcon : function(){
35884         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35885     },
35886
35887     /**
35888      * Clear any invalid styles/messages for this field
35889      */
35890     clearInvalid : function(){
35891         if(!this.rendered || this.preventMark){ // not rendered
35892             return;
35893         }
35894         this.el.removeClass(this.invalidClass);
35895         switch(this.msgTarget){
35896             case 'qtip':
35897                 this.el.dom.qtip = '';
35898                 break;
35899             case 'title':
35900                 this.el.dom.title = '';
35901                 break;
35902             case 'under':
35903                 if(this.errorEl){
35904                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35905                 }
35906                 break;
35907             case 'side':
35908                 if(this.errorIcon){
35909                     this.errorIcon.dom.qtip = '';
35910                     this.errorIcon.hide();
35911                     this.un('resize', this.alignErrorIcon, this);
35912                 }
35913                 break;
35914             default:
35915                 var t = Roo.getDom(this.msgTarget);
35916                 t.innerHTML = '';
35917                 t.style.display = 'none';
35918                 break;
35919         }
35920         this.fireEvent('valid', this);
35921     },
35922
35923     /**
35924      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35925      * @return {Mixed} value The field value
35926      */
35927     getRawValue : function(){
35928         var v = this.el.getValue();
35929         if(v === this.emptyText){
35930             v = '';
35931         }
35932         return v;
35933     },
35934
35935     /**
35936      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35937      * @return {Mixed} value The field value
35938      */
35939     getValue : function(){
35940         var v = this.el.getValue();
35941         if(v === this.emptyText || v === undefined){
35942             v = '';
35943         }
35944         return v;
35945     },
35946
35947     /**
35948      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35949      * @param {Mixed} value The value to set
35950      */
35951     setRawValue : function(v){
35952         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35953     },
35954
35955     /**
35956      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35957      * @param {Mixed} value The value to set
35958      */
35959     setValue : function(v){
35960         this.value = v;
35961         if(this.rendered){
35962             this.el.dom.value = (v === null || v === undefined ? '' : v);
35963              this.validate();
35964         }
35965     },
35966
35967     adjustSize : function(w, h){
35968         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35969         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35970         return s;
35971     },
35972
35973     adjustWidth : function(tag, w){
35974         tag = tag.toLowerCase();
35975         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35976             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35977                 if(tag == 'input'){
35978                     return w + 2;
35979                 }
35980                 if(tag = 'textarea'){
35981                     return w-2;
35982                 }
35983             }else if(Roo.isOpera){
35984                 if(tag == 'input'){
35985                     return w + 2;
35986                 }
35987                 if(tag = 'textarea'){
35988                     return w-2;
35989                 }
35990             }
35991         }
35992         return w;
35993     }
35994 });
35995
35996
35997 // anything other than normal should be considered experimental
35998 Roo.form.Field.msgFx = {
35999     normal : {
36000         show: function(msgEl, f){
36001             msgEl.setDisplayed('block');
36002         },
36003
36004         hide : function(msgEl, f){
36005             msgEl.setDisplayed(false).update('');
36006         }
36007     },
36008
36009     slide : {
36010         show: function(msgEl, f){
36011             msgEl.slideIn('t', {stopFx:true});
36012         },
36013
36014         hide : function(msgEl, f){
36015             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36016         }
36017     },
36018
36019     slideRight : {
36020         show: function(msgEl, f){
36021             msgEl.fixDisplay();
36022             msgEl.alignTo(f.el, 'tl-tr');
36023             msgEl.slideIn('l', {stopFx:true});
36024         },
36025
36026         hide : function(msgEl, f){
36027             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36028         }
36029     }
36030 };/*
36031  * Based on:
36032  * Ext JS Library 1.1.1
36033  * Copyright(c) 2006-2007, Ext JS, LLC.
36034  *
36035  * Originally Released Under LGPL - original licence link has changed is not relivant.
36036  *
36037  * Fork - LGPL
36038  * <script type="text/javascript">
36039  */
36040  
36041
36042 /**
36043  * @class Roo.form.TextField
36044  * @extends Roo.form.Field
36045  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36046  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36047  * @constructor
36048  * Creates a new TextField
36049  * @param {Object} config Configuration options
36050  */
36051 Roo.form.TextField = function(config){
36052     Roo.form.TextField.superclass.constructor.call(this, config);
36053     this.addEvents({
36054         /**
36055          * @event autosize
36056          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36057          * according to the default logic, but this event provides a hook for the developer to apply additional
36058          * logic at runtime to resize the field if needed.
36059              * @param {Roo.form.Field} this This text field
36060              * @param {Number} width The new field width
36061              */
36062         autosize : true
36063     });
36064 };
36065
36066 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36067     /**
36068      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36069      */
36070     grow : false,
36071     /**
36072      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36073      */
36074     growMin : 30,
36075     /**
36076      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36077      */
36078     growMax : 800,
36079     /**
36080      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36081      */
36082     vtype : null,
36083     /**
36084      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36085      */
36086     maskRe : null,
36087     /**
36088      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36089      */
36090     disableKeyFilter : false,
36091     /**
36092      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36093      */
36094     allowBlank : true,
36095     /**
36096      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36097      */
36098     minLength : 0,
36099     /**
36100      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36101      */
36102     maxLength : Number.MAX_VALUE,
36103     /**
36104      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36105      */
36106     minLengthText : "The minimum length for this field is {0}",
36107     /**
36108      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36109      */
36110     maxLengthText : "The maximum length for this field is {0}",
36111     /**
36112      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36113      */
36114     selectOnFocus : false,
36115     /**
36116      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36117      */
36118     blankText : "This field is required",
36119     /**
36120      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36121      * If available, this function will be called only after the basic validators all return true, and will be passed the
36122      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36123      */
36124     validator : null,
36125     /**
36126      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36127      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36128      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36129      */
36130     regex : null,
36131     /**
36132      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36133      */
36134     regexText : "",
36135     /**
36136      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36137      */
36138     emptyText : null,
36139     /**
36140      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36141      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36142      */
36143     emptyClass : 'x-form-empty-field',
36144
36145     // private
36146     initEvents : function(){
36147         Roo.form.TextField.superclass.initEvents.call(this);
36148         if(this.validationEvent == 'keyup'){
36149             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36150             this.el.on('keyup', this.filterValidation, this);
36151         }
36152         else if(this.validationEvent !== false){
36153             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36154         }
36155         if(this.selectOnFocus || this.emptyText){
36156             this.on("focus", this.preFocus, this);
36157             if(this.emptyText){
36158                 this.on('blur', this.postBlur, this);
36159                 this.applyEmptyText();
36160             }
36161         }
36162         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36163             this.el.on("keypress", this.filterKeys, this);
36164         }
36165         if(this.grow){
36166             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36167             this.el.on("click", this.autoSize,  this);
36168         }
36169     },
36170
36171     processValue : function(value){
36172         if(this.stripCharsRe){
36173             var newValue = value.replace(this.stripCharsRe, '');
36174             if(newValue !== value){
36175                 this.setRawValue(newValue);
36176                 return newValue;
36177             }
36178         }
36179         return value;
36180     },
36181
36182     filterValidation : function(e){
36183         if(!e.isNavKeyPress()){
36184             this.validationTask.delay(this.validationDelay);
36185         }
36186     },
36187
36188     // private
36189     onKeyUp : function(e){
36190         if(!e.isNavKeyPress()){
36191             this.autoSize();
36192         }
36193     },
36194
36195     /**
36196      * Resets the current field value to the originally-loaded value and clears any validation messages.
36197      * Also adds emptyText and emptyClass if the original value was blank.
36198      */
36199     reset : function(){
36200         Roo.form.TextField.superclass.reset.call(this);
36201         this.applyEmptyText();
36202     },
36203
36204     applyEmptyText : function(){
36205         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36206             this.setRawValue(this.emptyText);
36207             this.el.addClass(this.emptyClass);
36208         }
36209     },
36210
36211     // private
36212     preFocus : function(){
36213         if(this.emptyText){
36214             if(this.el.dom.value == this.emptyText){
36215                 this.setRawValue('');
36216             }
36217             this.el.removeClass(this.emptyClass);
36218         }
36219         if(this.selectOnFocus){
36220             this.el.dom.select();
36221         }
36222     },
36223
36224     // private
36225     postBlur : function(){
36226         this.applyEmptyText();
36227     },
36228
36229     // private
36230     filterKeys : function(e){
36231         var k = e.getKey();
36232         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36233             return;
36234         }
36235         var c = e.getCharCode(), cc = String.fromCharCode(c);
36236         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36237             return;
36238         }
36239         if(!this.maskRe.test(cc)){
36240             e.stopEvent();
36241         }
36242     },
36243
36244     setValue : function(v){
36245         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36246             this.el.removeClass(this.emptyClass);
36247         }
36248         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36249         this.applyEmptyText();
36250         this.autoSize();
36251     },
36252
36253     /**
36254      * Validates a value according to the field's validation rules and marks the field as invalid
36255      * if the validation fails
36256      * @param {Mixed} value The value to validate
36257      * @return {Boolean} True if the value is valid, else false
36258      */
36259     validateValue : function(value){
36260         if(value.length < 1 || value === this.emptyText){ // if it's blank
36261              if(this.allowBlank){
36262                 this.clearInvalid();
36263                 return true;
36264              }else{
36265                 this.markInvalid(this.blankText);
36266                 return false;
36267              }
36268         }
36269         if(value.length < this.minLength){
36270             this.markInvalid(String.format(this.minLengthText, this.minLength));
36271             return false;
36272         }
36273         if(value.length > this.maxLength){
36274             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36275             return false;
36276         }
36277         if(this.vtype){
36278             var vt = Roo.form.VTypes;
36279             if(!vt[this.vtype](value, this)){
36280                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36281                 return false;
36282             }
36283         }
36284         if(typeof this.validator == "function"){
36285             var msg = this.validator(value);
36286             if(msg !== true){
36287                 this.markInvalid(msg);
36288                 return false;
36289             }
36290         }
36291         if(this.regex && !this.regex.test(value)){
36292             this.markInvalid(this.regexText);
36293             return false;
36294         }
36295         return true;
36296     },
36297
36298     /**
36299      * Selects text in this field
36300      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36301      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36302      */
36303     selectText : function(start, end){
36304         var v = this.getRawValue();
36305         if(v.length > 0){
36306             start = start === undefined ? 0 : start;
36307             end = end === undefined ? v.length : end;
36308             var d = this.el.dom;
36309             if(d.setSelectionRange){
36310                 d.setSelectionRange(start, end);
36311             }else if(d.createTextRange){
36312                 var range = d.createTextRange();
36313                 range.moveStart("character", start);
36314                 range.moveEnd("character", v.length-end);
36315                 range.select();
36316             }
36317         }
36318     },
36319
36320     /**
36321      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36322      * This only takes effect if grow = true, and fires the autosize event.
36323      */
36324     autoSize : function(){
36325         if(!this.grow || !this.rendered){
36326             return;
36327         }
36328         if(!this.metrics){
36329             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36330         }
36331         var el = this.el;
36332         var v = el.dom.value;
36333         var d = document.createElement('div');
36334         d.appendChild(document.createTextNode(v));
36335         v = d.innerHTML;
36336         d = null;
36337         v += "&#160;";
36338         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36339         this.el.setWidth(w);
36340         this.fireEvent("autosize", this, w);
36341     }
36342 });/*
36343  * Based on:
36344  * Ext JS Library 1.1.1
36345  * Copyright(c) 2006-2007, Ext JS, LLC.
36346  *
36347  * Originally Released Under LGPL - original licence link has changed is not relivant.
36348  *
36349  * Fork - LGPL
36350  * <script type="text/javascript">
36351  */
36352  
36353 /**
36354  * @class Roo.form.Hidden
36355  * @extends Roo.form.TextField
36356  * Simple Hidden element used on forms 
36357  * 
36358  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36359  * 
36360  * @constructor
36361  * Creates a new Hidden form element.
36362  * @param {Object} config Configuration options
36363  */
36364
36365
36366
36367 // easy hidden field...
36368 Roo.form.Hidden = function(config){
36369     Roo.form.Hidden.superclass.constructor.call(this, config);
36370 };
36371   
36372 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36373     fieldLabel:      '',
36374     inputType:      'hidden',
36375     width:          50,
36376     allowBlank:     true,
36377     labelSeparator: '',
36378     hidden:         true,
36379     itemCls :       'x-form-item-display-none'
36380
36381
36382 });
36383
36384
36385 /*
36386  * Based on:
36387  * Ext JS Library 1.1.1
36388  * Copyright(c) 2006-2007, Ext JS, LLC.
36389  *
36390  * Originally Released Under LGPL - original licence link has changed is not relivant.
36391  *
36392  * Fork - LGPL
36393  * <script type="text/javascript">
36394  */
36395  
36396 /**
36397  * @class Roo.form.TriggerField
36398  * @extends Roo.form.TextField
36399  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36400  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36401  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36402  * for which you can provide a custom implementation.  For example:
36403  * <pre><code>
36404 var trigger = new Roo.form.TriggerField();
36405 trigger.onTriggerClick = myTriggerFn;
36406 trigger.applyTo('my-field');
36407 </code></pre>
36408  *
36409  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36410  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36411  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36412  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36413  * @constructor
36414  * Create a new TriggerField.
36415  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36416  * to the base TextField)
36417  */
36418 Roo.form.TriggerField = function(config){
36419     this.mimicing = false;
36420     Roo.form.TriggerField.superclass.constructor.call(this, config);
36421 };
36422
36423 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36424     /**
36425      * @cfg {String} triggerClass A CSS class to apply to the trigger
36426      */
36427     /**
36428      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36429      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36430      */
36431     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36432     /**
36433      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36434      */
36435     hideTrigger:false,
36436
36437     /** @cfg {Boolean} grow @hide */
36438     /** @cfg {Number} growMin @hide */
36439     /** @cfg {Number} growMax @hide */
36440
36441     /**
36442      * @hide 
36443      * @method
36444      */
36445     autoSize: Roo.emptyFn,
36446     // private
36447     monitorTab : true,
36448     // private
36449     deferHeight : true,
36450
36451     
36452     actionMode : 'wrap',
36453     // private
36454     onResize : function(w, h){
36455         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36456         if(typeof w == 'number'){
36457             var x = w - this.trigger.getWidth();
36458             this.el.setWidth(this.adjustWidth('input', x));
36459             this.trigger.setStyle('left', x+'px');
36460         }
36461     },
36462
36463     // private
36464     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36465
36466     // private
36467     getResizeEl : function(){
36468         return this.wrap;
36469     },
36470
36471     // private
36472     getPositionEl : function(){
36473         return this.wrap;
36474     },
36475
36476     // private
36477     alignErrorIcon : function(){
36478         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36479     },
36480
36481     // private
36482     onRender : function(ct, position){
36483         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36484         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36485         this.trigger = this.wrap.createChild(this.triggerConfig ||
36486                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36487         if(this.hideTrigger){
36488             this.trigger.setDisplayed(false);
36489         }
36490         this.initTrigger();
36491         if(!this.width){
36492             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36493         }
36494     },
36495
36496     // private
36497     initTrigger : function(){
36498         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36499         this.trigger.addClassOnOver('x-form-trigger-over');
36500         this.trigger.addClassOnClick('x-form-trigger-click');
36501     },
36502
36503     // private
36504     onDestroy : function(){
36505         if(this.trigger){
36506             this.trigger.removeAllListeners();
36507             this.trigger.remove();
36508         }
36509         if(this.wrap){
36510             this.wrap.remove();
36511         }
36512         Roo.form.TriggerField.superclass.onDestroy.call(this);
36513     },
36514
36515     // private
36516     onFocus : function(){
36517         Roo.form.TriggerField.superclass.onFocus.call(this);
36518         if(!this.mimicing){
36519             this.wrap.addClass('x-trigger-wrap-focus');
36520             this.mimicing = true;
36521             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36522             if(this.monitorTab){
36523                 this.el.on("keydown", this.checkTab, this);
36524             }
36525         }
36526     },
36527
36528     // private
36529     checkTab : function(e){
36530         if(e.getKey() == e.TAB){
36531             this.triggerBlur();
36532         }
36533     },
36534
36535     // private
36536     onBlur : function(){
36537         // do nothing
36538     },
36539
36540     // private
36541     mimicBlur : function(e, t){
36542         if(!this.wrap.contains(t) && this.validateBlur()){
36543             this.triggerBlur();
36544         }
36545     },
36546
36547     // private
36548     triggerBlur : function(){
36549         this.mimicing = false;
36550         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36551         if(this.monitorTab){
36552             this.el.un("keydown", this.checkTab, this);
36553         }
36554         this.wrap.removeClass('x-trigger-wrap-focus');
36555         Roo.form.TriggerField.superclass.onBlur.call(this);
36556     },
36557
36558     // private
36559     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36560     validateBlur : function(e, t){
36561         return true;
36562     },
36563
36564     // private
36565     onDisable : function(){
36566         Roo.form.TriggerField.superclass.onDisable.call(this);
36567         if(this.wrap){
36568             this.wrap.addClass('x-item-disabled');
36569         }
36570     },
36571
36572     // private
36573     onEnable : function(){
36574         Roo.form.TriggerField.superclass.onEnable.call(this);
36575         if(this.wrap){
36576             this.wrap.removeClass('x-item-disabled');
36577         }
36578     },
36579
36580     // private
36581     onShow : function(){
36582         var ae = this.getActionEl();
36583         
36584         if(ae){
36585             ae.dom.style.display = '';
36586             ae.dom.style.visibility = 'visible';
36587         }
36588     },
36589
36590     // private
36591     
36592     onHide : function(){
36593         var ae = this.getActionEl();
36594         ae.dom.style.display = 'none';
36595     },
36596
36597     /**
36598      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36599      * by an implementing function.
36600      * @method
36601      * @param {EventObject} e
36602      */
36603     onTriggerClick : Roo.emptyFn
36604 });
36605
36606 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36607 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36608 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36609 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36610     initComponent : function(){
36611         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36612
36613         this.triggerConfig = {
36614             tag:'span', cls:'x-form-twin-triggers', cn:[
36615             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36616             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36617         ]};
36618     },
36619
36620     getTrigger : function(index){
36621         return this.triggers[index];
36622     },
36623
36624     initTrigger : function(){
36625         var ts = this.trigger.select('.x-form-trigger', true);
36626         this.wrap.setStyle('overflow', 'hidden');
36627         var triggerField = this;
36628         ts.each(function(t, all, index){
36629             t.hide = function(){
36630                 var w = triggerField.wrap.getWidth();
36631                 this.dom.style.display = 'none';
36632                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36633             };
36634             t.show = function(){
36635                 var w = triggerField.wrap.getWidth();
36636                 this.dom.style.display = '';
36637                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36638             };
36639             var triggerIndex = 'Trigger'+(index+1);
36640
36641             if(this['hide'+triggerIndex]){
36642                 t.dom.style.display = 'none';
36643             }
36644             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36645             t.addClassOnOver('x-form-trigger-over');
36646             t.addClassOnClick('x-form-trigger-click');
36647         }, this);
36648         this.triggers = ts.elements;
36649     },
36650
36651     onTrigger1Click : Roo.emptyFn,
36652     onTrigger2Click : Roo.emptyFn
36653 });/*
36654  * Based on:
36655  * Ext JS Library 1.1.1
36656  * Copyright(c) 2006-2007, Ext JS, LLC.
36657  *
36658  * Originally Released Under LGPL - original licence link has changed is not relivant.
36659  *
36660  * Fork - LGPL
36661  * <script type="text/javascript">
36662  */
36663  
36664 /**
36665  * @class Roo.form.TextArea
36666  * @extends Roo.form.TextField
36667  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36668  * support for auto-sizing.
36669  * @constructor
36670  * Creates a new TextArea
36671  * @param {Object} config Configuration options
36672  */
36673 Roo.form.TextArea = function(config){
36674     Roo.form.TextArea.superclass.constructor.call(this, config);
36675     // these are provided exchanges for backwards compat
36676     // minHeight/maxHeight were replaced by growMin/growMax to be
36677     // compatible with TextField growing config values
36678     if(this.minHeight !== undefined){
36679         this.growMin = this.minHeight;
36680     }
36681     if(this.maxHeight !== undefined){
36682         this.growMax = this.maxHeight;
36683     }
36684 };
36685
36686 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36687     /**
36688      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36689      */
36690     growMin : 60,
36691     /**
36692      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36693      */
36694     growMax: 1000,
36695     /**
36696      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36697      * in the field (equivalent to setting overflow: hidden, defaults to false)
36698      */
36699     preventScrollbars: false,
36700     /**
36701      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36702      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36703      */
36704
36705     // private
36706     onRender : function(ct, position){
36707         if(!this.el){
36708             this.defaultAutoCreate = {
36709                 tag: "textarea",
36710                 style:"width:300px;height:60px;",
36711                 autocomplete: "off"
36712             };
36713         }
36714         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36715         if(this.grow){
36716             this.textSizeEl = Roo.DomHelper.append(document.body, {
36717                 tag: "pre", cls: "x-form-grow-sizer"
36718             });
36719             if(this.preventScrollbars){
36720                 this.el.setStyle("overflow", "hidden");
36721             }
36722             this.el.setHeight(this.growMin);
36723         }
36724     },
36725
36726     onDestroy : function(){
36727         if(this.textSizeEl){
36728             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36729         }
36730         Roo.form.TextArea.superclass.onDestroy.call(this);
36731     },
36732
36733     // private
36734     onKeyUp : function(e){
36735         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36736             this.autoSize();
36737         }
36738     },
36739
36740     /**
36741      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36742      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36743      */
36744     autoSize : function(){
36745         if(!this.grow || !this.textSizeEl){
36746             return;
36747         }
36748         var el = this.el;
36749         var v = el.dom.value;
36750         var ts = this.textSizeEl;
36751
36752         ts.innerHTML = '';
36753         ts.appendChild(document.createTextNode(v));
36754         v = ts.innerHTML;
36755
36756         Roo.fly(ts).setWidth(this.el.getWidth());
36757         if(v.length < 1){
36758             v = "&#160;&#160;";
36759         }else{
36760             if(Roo.isIE){
36761                 v = v.replace(/\n/g, '<p>&#160;</p>');
36762             }
36763             v += "&#160;\n&#160;";
36764         }
36765         ts.innerHTML = v;
36766         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36767         if(h != this.lastHeight){
36768             this.lastHeight = h;
36769             this.el.setHeight(h);
36770             this.fireEvent("autosize", this, h);
36771         }
36772     }
36773 });/*
36774  * Based on:
36775  * Ext JS Library 1.1.1
36776  * Copyright(c) 2006-2007, Ext JS, LLC.
36777  *
36778  * Originally Released Under LGPL - original licence link has changed is not relivant.
36779  *
36780  * Fork - LGPL
36781  * <script type="text/javascript">
36782  */
36783  
36784
36785 /**
36786  * @class Roo.form.NumberField
36787  * @extends Roo.form.TextField
36788  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36789  * @constructor
36790  * Creates a new NumberField
36791  * @param {Object} config Configuration options
36792  */
36793 Roo.form.NumberField = function(config){
36794     Roo.form.NumberField.superclass.constructor.call(this, config);
36795 };
36796
36797 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36798     /**
36799      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36800      */
36801     fieldClass: "x-form-field x-form-num-field",
36802     /**
36803      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36804      */
36805     allowDecimals : true,
36806     /**
36807      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36808      */
36809     decimalSeparator : ".",
36810     /**
36811      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36812      */
36813     decimalPrecision : 2,
36814     /**
36815      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36816      */
36817     allowNegative : true,
36818     /**
36819      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36820      */
36821     minValue : Number.NEGATIVE_INFINITY,
36822     /**
36823      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36824      */
36825     maxValue : Number.MAX_VALUE,
36826     /**
36827      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36828      */
36829     minText : "The minimum value for this field is {0}",
36830     /**
36831      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36832      */
36833     maxText : "The maximum value for this field is {0}",
36834     /**
36835      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36836      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36837      */
36838     nanText : "{0} is not a valid number",
36839
36840     // private
36841     initEvents : function(){
36842         Roo.form.NumberField.superclass.initEvents.call(this);
36843         var allowed = "0123456789";
36844         if(this.allowDecimals){
36845             allowed += this.decimalSeparator;
36846         }
36847         if(this.allowNegative){
36848             allowed += "-";
36849         }
36850         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36851         var keyPress = function(e){
36852             var k = e.getKey();
36853             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36854                 return;
36855             }
36856             var c = e.getCharCode();
36857             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36858                 e.stopEvent();
36859             }
36860         };
36861         this.el.on("keypress", keyPress, this);
36862     },
36863
36864     // private
36865     validateValue : function(value){
36866         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36867             return false;
36868         }
36869         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36870              return true;
36871         }
36872         var num = this.parseValue(value);
36873         if(isNaN(num)){
36874             this.markInvalid(String.format(this.nanText, value));
36875             return false;
36876         }
36877         if(num < this.minValue){
36878             this.markInvalid(String.format(this.minText, this.minValue));
36879             return false;
36880         }
36881         if(num > this.maxValue){
36882             this.markInvalid(String.format(this.maxText, this.maxValue));
36883             return false;
36884         }
36885         return true;
36886     },
36887
36888     getValue : function(){
36889         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36890     },
36891
36892     // private
36893     parseValue : function(value){
36894         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36895         return isNaN(value) ? '' : value;
36896     },
36897
36898     // private
36899     fixPrecision : function(value){
36900         var nan = isNaN(value);
36901         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36902             return nan ? '' : value;
36903         }
36904         return parseFloat(value).toFixed(this.decimalPrecision);
36905     },
36906
36907     setValue : function(v){
36908         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36909     },
36910
36911     // private
36912     decimalPrecisionFcn : function(v){
36913         return Math.floor(v);
36914     },
36915
36916     beforeBlur : function(){
36917         var v = this.parseValue(this.getRawValue());
36918         if(v){
36919             this.setValue(this.fixPrecision(v));
36920         }
36921     }
36922 });/*
36923  * Based on:
36924  * Ext JS Library 1.1.1
36925  * Copyright(c) 2006-2007, Ext JS, LLC.
36926  *
36927  * Originally Released Under LGPL - original licence link has changed is not relivant.
36928  *
36929  * Fork - LGPL
36930  * <script type="text/javascript">
36931  */
36932  
36933 /**
36934  * @class Roo.form.DateField
36935  * @extends Roo.form.TriggerField
36936  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36937 * @constructor
36938 * Create a new DateField
36939 * @param {Object} config
36940  */
36941 Roo.form.DateField = function(config){
36942     Roo.form.DateField.superclass.constructor.call(this, config);
36943     
36944       this.addEvents({
36945          
36946         /**
36947          * @event select
36948          * Fires when a date is selected
36949              * @param {Roo.form.DateField} combo This combo box
36950              * @param {Date} date The date selected
36951              */
36952         'select' : true
36953          
36954     });
36955     
36956     
36957     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36958     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36959     this.ddMatch = null;
36960     if(this.disabledDates){
36961         var dd = this.disabledDates;
36962         var re = "(?:";
36963         for(var i = 0; i < dd.length; i++){
36964             re += dd[i];
36965             if(i != dd.length-1) re += "|";
36966         }
36967         this.ddMatch = new RegExp(re + ")");
36968     }
36969 };
36970
36971 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36972     /**
36973      * @cfg {String} format
36974      * The default date format string which can be overriden for localization support.  The format must be
36975      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36976      */
36977     format : "m/d/y",
36978     /**
36979      * @cfg {String} altFormats
36980      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36981      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36982      */
36983     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36984     /**
36985      * @cfg {Array} disabledDays
36986      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36987      */
36988     disabledDays : null,
36989     /**
36990      * @cfg {String} disabledDaysText
36991      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36992      */
36993     disabledDaysText : "Disabled",
36994     /**
36995      * @cfg {Array} disabledDates
36996      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36997      * expression so they are very powerful. Some examples:
36998      * <ul>
36999      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
37000      * <li>["03/08", "09/16"] would disable those days for every year</li>
37001      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
37002      * <li>["03/../2006"] would disable every day in March 2006</li>
37003      * <li>["^03"] would disable every day in every March</li>
37004      * </ul>
37005      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37006      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37007      */
37008     disabledDates : null,
37009     /**
37010      * @cfg {String} disabledDatesText
37011      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37012      */
37013     disabledDatesText : "Disabled",
37014     /**
37015      * @cfg {Date/String} minValue
37016      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37017      * valid format (defaults to null).
37018      */
37019     minValue : null,
37020     /**
37021      * @cfg {Date/String} maxValue
37022      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37023      * valid format (defaults to null).
37024      */
37025     maxValue : null,
37026     /**
37027      * @cfg {String} minText
37028      * The error text to display when the date in the cell is before minValue (defaults to
37029      * 'The date in this field must be after {minValue}').
37030      */
37031     minText : "The date in this field must be equal to or after {0}",
37032     /**
37033      * @cfg {String} maxText
37034      * The error text to display when the date in the cell is after maxValue (defaults to
37035      * 'The date in this field must be before {maxValue}').
37036      */
37037     maxText : "The date in this field must be equal to or before {0}",
37038     /**
37039      * @cfg {String} invalidText
37040      * The error text to display when the date in the field is invalid (defaults to
37041      * '{value} is not a valid date - it must be in the format {format}').
37042      */
37043     invalidText : "{0} is not a valid date - it must be in the format {1}",
37044     /**
37045      * @cfg {String} triggerClass
37046      * An additional CSS class used to style the trigger button.  The trigger will always get the
37047      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37048      * which displays a calendar icon).
37049      */
37050     triggerClass : 'x-form-date-trigger',
37051     
37052
37053     /**
37054      * @cfg {bool} useIso
37055      * if enabled, then the date field will use a hidden field to store the 
37056      * real value as iso formated date. default (false)
37057      */ 
37058     useIso : false,
37059     /**
37060      * @cfg {String/Object} autoCreate
37061      * A DomHelper element spec, or true for a default element spec (defaults to
37062      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37063      */ 
37064     // private
37065     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37066     
37067     // private
37068     hiddenField: false,
37069     
37070     onRender : function(ct, position)
37071     {
37072         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37073         if (this.useIso) {
37074             this.el.dom.removeAttribute('name'); 
37075             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37076                     'before', true);
37077             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37078             // prevent input submission
37079             this.hiddenName = this.name;
37080         }
37081             
37082             
37083     },
37084     
37085     // private
37086     validateValue : function(value)
37087     {
37088         value = this.formatDate(value);
37089         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37090             return false;
37091         }
37092         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37093              return true;
37094         }
37095         var svalue = value;
37096         value = this.parseDate(value);
37097         if(!value){
37098             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37099             return false;
37100         }
37101         var time = value.getTime();
37102         if(this.minValue && time < this.minValue.getTime()){
37103             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37104             return false;
37105         }
37106         if(this.maxValue && time > this.maxValue.getTime()){
37107             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37108             return false;
37109         }
37110         if(this.disabledDays){
37111             var day = value.getDay();
37112             for(var i = 0; i < this.disabledDays.length; i++) {
37113                 if(day === this.disabledDays[i]){
37114                     this.markInvalid(this.disabledDaysText);
37115                     return false;
37116                 }
37117             }
37118         }
37119         var fvalue = this.formatDate(value);
37120         if(this.ddMatch && this.ddMatch.test(fvalue)){
37121             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37122             return false;
37123         }
37124         return true;
37125     },
37126
37127     // private
37128     // Provides logic to override the default TriggerField.validateBlur which just returns true
37129     validateBlur : function(){
37130         return !this.menu || !this.menu.isVisible();
37131     },
37132
37133     /**
37134      * Returns the current date value of the date field.
37135      * @return {Date} The date value
37136      */
37137     getValue : function(){
37138         
37139         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37140     },
37141
37142     /**
37143      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37144      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37145      * (the default format used is "m/d/y").
37146      * <br />Usage:
37147      * <pre><code>
37148 //All of these calls set the same date value (May 4, 2006)
37149
37150 //Pass a date object:
37151 var dt = new Date('5/4/06');
37152 dateField.setValue(dt);
37153
37154 //Pass a date string (default format):
37155 dateField.setValue('5/4/06');
37156
37157 //Pass a date string (custom format):
37158 dateField.format = 'Y-m-d';
37159 dateField.setValue('2006-5-4');
37160 </code></pre>
37161      * @param {String/Date} date The date or valid date string
37162      */
37163     setValue : function(date){
37164         if (this.hiddenField) {
37165             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37166         }
37167         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37168     },
37169
37170     // private
37171     parseDate : function(value){
37172         if(!value || value instanceof Date){
37173             return value;
37174         }
37175         var v = Date.parseDate(value, this.format);
37176         if(!v && this.altFormats){
37177             if(!this.altFormatsArray){
37178                 this.altFormatsArray = this.altFormats.split("|");
37179             }
37180             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37181                 v = Date.parseDate(value, this.altFormatsArray[i]);
37182             }
37183         }
37184         return v;
37185     },
37186
37187     // private
37188     formatDate : function(date, fmt){
37189         return (!date || !(date instanceof Date)) ?
37190                date : date.dateFormat(fmt || this.format);
37191     },
37192
37193     // private
37194     menuListeners : {
37195         select: function(m, d){
37196             this.setValue(d);
37197             this.fireEvent('select', this, d);
37198         },
37199         show : function(){ // retain focus styling
37200             this.onFocus();
37201         },
37202         hide : function(){
37203             this.focus.defer(10, this);
37204             var ml = this.menuListeners;
37205             this.menu.un("select", ml.select,  this);
37206             this.menu.un("show", ml.show,  this);
37207             this.menu.un("hide", ml.hide,  this);
37208         }
37209     },
37210
37211     // private
37212     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37213     onTriggerClick : function(){
37214         if(this.disabled){
37215             return;
37216         }
37217         if(this.menu == null){
37218             this.menu = new Roo.menu.DateMenu();
37219         }
37220         Roo.apply(this.menu.picker,  {
37221             showClear: this.allowBlank,
37222             minDate : this.minValue,
37223             maxDate : this.maxValue,
37224             disabledDatesRE : this.ddMatch,
37225             disabledDatesText : this.disabledDatesText,
37226             disabledDays : this.disabledDays,
37227             disabledDaysText : this.disabledDaysText,
37228             format : this.format,
37229             minText : String.format(this.minText, this.formatDate(this.minValue)),
37230             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37231         });
37232         this.menu.on(Roo.apply({}, this.menuListeners, {
37233             scope:this
37234         }));
37235         this.menu.picker.setValue(this.getValue() || new Date());
37236         this.menu.show(this.el, "tl-bl?");
37237     },
37238
37239     beforeBlur : function(){
37240         var v = this.parseDate(this.getRawValue());
37241         if(v){
37242             this.setValue(v);
37243         }
37244     }
37245
37246     /** @cfg {Boolean} grow @hide */
37247     /** @cfg {Number} growMin @hide */
37248     /** @cfg {Number} growMax @hide */
37249     /**
37250      * @hide
37251      * @method autoSize
37252      */
37253 });/*
37254  * Based on:
37255  * Ext JS Library 1.1.1
37256  * Copyright(c) 2006-2007, Ext JS, LLC.
37257  *
37258  * Originally Released Under LGPL - original licence link has changed is not relivant.
37259  *
37260  * Fork - LGPL
37261  * <script type="text/javascript">
37262  */
37263  
37264
37265 /**
37266  * @class Roo.form.ComboBox
37267  * @extends Roo.form.TriggerField
37268  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37269  * @constructor
37270  * Create a new ComboBox.
37271  * @param {Object} config Configuration options
37272  */
37273 Roo.form.ComboBox = function(config){
37274     Roo.form.ComboBox.superclass.constructor.call(this, config);
37275     this.addEvents({
37276         /**
37277          * @event expand
37278          * Fires when the dropdown list is expanded
37279              * @param {Roo.form.ComboBox} combo This combo box
37280              */
37281         'expand' : true,
37282         /**
37283          * @event collapse
37284          * Fires when the dropdown list is collapsed
37285              * @param {Roo.form.ComboBox} combo This combo box
37286              */
37287         'collapse' : true,
37288         /**
37289          * @event beforeselect
37290          * Fires before a list item is selected. Return false to cancel the selection.
37291              * @param {Roo.form.ComboBox} combo This combo box
37292              * @param {Roo.data.Record} record The data record returned from the underlying store
37293              * @param {Number} index The index of the selected item in the dropdown list
37294              */
37295         'beforeselect' : true,
37296         /**
37297          * @event select
37298          * Fires when a list item is selected
37299              * @param {Roo.form.ComboBox} combo This combo box
37300              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37301              * @param {Number} index The index of the selected item in the dropdown list
37302              */
37303         'select' : true,
37304         /**
37305          * @event beforequery
37306          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37307          * The event object passed has these properties:
37308              * @param {Roo.form.ComboBox} combo This combo box
37309              * @param {String} query The query
37310              * @param {Boolean} forceAll true to force "all" query
37311              * @param {Boolean} cancel true to cancel the query
37312              * @param {Object} e The query event object
37313              */
37314         'beforequery': true,
37315          /**
37316          * @event add
37317          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37318              * @param {Roo.form.ComboBox} combo This combo box
37319              */
37320         'add' : true,
37321         /**
37322          * @event edit
37323          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37324              * @param {Roo.form.ComboBox} combo This combo box
37325              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37326              */
37327         'edit' : true
37328         
37329         
37330     });
37331     if(this.transform){
37332         this.allowDomMove = false;
37333         var s = Roo.getDom(this.transform);
37334         if(!this.hiddenName){
37335             this.hiddenName = s.name;
37336         }
37337         if(!this.store){
37338             this.mode = 'local';
37339             var d = [], opts = s.options;
37340             for(var i = 0, len = opts.length;i < len; i++){
37341                 var o = opts[i];
37342                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37343                 if(o.selected) {
37344                     this.value = value;
37345                 }
37346                 d.push([value, o.text]);
37347             }
37348             this.store = new Roo.data.SimpleStore({
37349                 'id': 0,
37350                 fields: ['value', 'text'],
37351                 data : d
37352             });
37353             this.valueField = 'value';
37354             this.displayField = 'text';
37355         }
37356         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37357         if(!this.lazyRender){
37358             this.target = true;
37359             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37360             s.parentNode.removeChild(s); // remove it
37361             this.render(this.el.parentNode);
37362         }else{
37363             s.parentNode.removeChild(s); // remove it
37364         }
37365
37366     }
37367     if (this.store) {
37368         this.store = Roo.factory(this.store, Roo.data);
37369     }
37370     
37371     this.selectedIndex = -1;
37372     if(this.mode == 'local'){
37373         if(config.queryDelay === undefined){
37374             this.queryDelay = 10;
37375         }
37376         if(config.minChars === undefined){
37377             this.minChars = 0;
37378         }
37379     }
37380 };
37381
37382 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37383     /**
37384      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37385      */
37386     /**
37387      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37388      * rendering into an Roo.Editor, defaults to false)
37389      */
37390     /**
37391      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37392      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37393      */
37394     /**
37395      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37396      */
37397     /**
37398      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37399      * the dropdown list (defaults to undefined, with no header element)
37400      */
37401
37402      /**
37403      * @cfg {String/Roo.Template} tpl The template to use to render the output
37404      */
37405      
37406     // private
37407     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37408     /**
37409      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37410      */
37411     listWidth: undefined,
37412     /**
37413      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37414      * mode = 'remote' or 'text' if mode = 'local')
37415      */
37416     displayField: undefined,
37417     /**
37418      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37419      * mode = 'remote' or 'value' if mode = 'local'). 
37420      * Note: use of a valueField requires the user make a selection
37421      * in order for a value to be mapped.
37422      */
37423     valueField: undefined,
37424     
37425     
37426     /**
37427      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37428      * field's data value (defaults to the underlying DOM element's name)
37429      */
37430     hiddenName: undefined,
37431     /**
37432      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37433      */
37434     listClass: '',
37435     /**
37436      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37437      */
37438     selectedClass: 'x-combo-selected',
37439     /**
37440      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37441      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37442      * which displays a downward arrow icon).
37443      */
37444     triggerClass : 'x-form-arrow-trigger',
37445     /**
37446      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37447      */
37448     shadow:'sides',
37449     /**
37450      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37451      * anchor positions (defaults to 'tl-bl')
37452      */
37453     listAlign: 'tl-bl?',
37454     /**
37455      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37456      */
37457     maxHeight: 300,
37458     /**
37459      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37460      * query specified by the allQuery config option (defaults to 'query')
37461      */
37462     triggerAction: 'query',
37463     /**
37464      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37465      * (defaults to 4, does not apply if editable = false)
37466      */
37467     minChars : 4,
37468     /**
37469      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37470      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37471      */
37472     typeAhead: false,
37473     /**
37474      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37475      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37476      */
37477     queryDelay: 500,
37478     /**
37479      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37480      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37481      */
37482     pageSize: 0,
37483     /**
37484      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37485      * when editable = true (defaults to false)
37486      */
37487     selectOnFocus:false,
37488     /**
37489      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37490      */
37491     queryParam: 'query',
37492     /**
37493      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37494      * when mode = 'remote' (defaults to 'Loading...')
37495      */
37496     loadingText: 'Loading...',
37497     /**
37498      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37499      */
37500     resizable: false,
37501     /**
37502      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37503      */
37504     handleHeight : 8,
37505     /**
37506      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37507      * traditional select (defaults to true)
37508      */
37509     editable: true,
37510     /**
37511      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37512      */
37513     allQuery: '',
37514     /**
37515      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37516      */
37517     mode: 'remote',
37518     /**
37519      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37520      * listWidth has a higher value)
37521      */
37522     minListWidth : 70,
37523     /**
37524      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37525      * allow the user to set arbitrary text into the field (defaults to false)
37526      */
37527     forceSelection:false,
37528     /**
37529      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37530      * if typeAhead = true (defaults to 250)
37531      */
37532     typeAheadDelay : 250,
37533     /**
37534      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37535      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37536      */
37537     valueNotFoundText : undefined,
37538     /**
37539      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37540      */
37541     blockFocus : false,
37542     
37543     /**
37544      * @cfg {Boolean} disableClear Disable showing of clear button.
37545      */
37546     disableClear : false,
37547     /**
37548      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37549      */
37550     alwaysQuery : false,
37551     
37552     //private
37553     addicon : false,
37554     editicon: false,
37555     
37556     // element that contains real text value.. (when hidden is used..)
37557      
37558     // private
37559     onRender : function(ct, position){
37560         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37561         if(this.hiddenName){
37562             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37563                     'before', true);
37564             this.hiddenField.value =
37565                 this.hiddenValue !== undefined ? this.hiddenValue :
37566                 this.value !== undefined ? this.value : '';
37567
37568             // prevent input submission
37569             this.el.dom.removeAttribute('name');
37570              
37571              
37572         }
37573         if(Roo.isGecko){
37574             this.el.dom.setAttribute('autocomplete', 'off');
37575         }
37576
37577         var cls = 'x-combo-list';
37578
37579         this.list = new Roo.Layer({
37580             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37581         });
37582
37583         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37584         this.list.setWidth(lw);
37585         this.list.swallowEvent('mousewheel');
37586         this.assetHeight = 0;
37587
37588         if(this.title){
37589             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37590             this.assetHeight += this.header.getHeight();
37591         }
37592
37593         this.innerList = this.list.createChild({cls:cls+'-inner'});
37594         this.innerList.on('mouseover', this.onViewOver, this);
37595         this.innerList.on('mousemove', this.onViewMove, this);
37596         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37597         
37598         if(this.allowBlank && !this.pageSize && !this.disableClear){
37599             this.footer = this.list.createChild({cls:cls+'-ft'});
37600             this.pageTb = new Roo.Toolbar(this.footer);
37601            
37602         }
37603         if(this.pageSize){
37604             this.footer = this.list.createChild({cls:cls+'-ft'});
37605             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37606                     {pageSize: this.pageSize});
37607             
37608         }
37609         
37610         if (this.pageTb && this.allowBlank && !this.disableClear) {
37611             var _this = this;
37612             this.pageTb.add(new Roo.Toolbar.Fill(), {
37613                 cls: 'x-btn-icon x-btn-clear',
37614                 text: '&#160;',
37615                 handler: function()
37616                 {
37617                     _this.collapse();
37618                     _this.clearValue();
37619                     _this.onSelect(false, -1);
37620                 }
37621             });
37622         }
37623         if (this.footer) {
37624             this.assetHeight += this.footer.getHeight();
37625         }
37626         
37627
37628         if(!this.tpl){
37629             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37630         }
37631
37632         this.view = new Roo.View(this.innerList, this.tpl, {
37633             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37634         });
37635
37636         this.view.on('click', this.onViewClick, this);
37637
37638         this.store.on('beforeload', this.onBeforeLoad, this);
37639         this.store.on('load', this.onLoad, this);
37640         this.store.on('loadexception', this.collapse, this);
37641
37642         if(this.resizable){
37643             this.resizer = new Roo.Resizable(this.list,  {
37644                pinned:true, handles:'se'
37645             });
37646             this.resizer.on('resize', function(r, w, h){
37647                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37648                 this.listWidth = w;
37649                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37650                 this.restrictHeight();
37651             }, this);
37652             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37653         }
37654         if(!this.editable){
37655             this.editable = true;
37656             this.setEditable(false);
37657         }  
37658         
37659         
37660         if (typeof(this.events.add.listeners) != 'undefined') {
37661             
37662             this.addicon = this.wrap.createChild(
37663                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37664        
37665             this.addicon.on('click', function(e) {
37666                 this.fireEvent('add', this);
37667             }, this);
37668         }
37669         if (typeof(this.events.edit.listeners) != 'undefined') {
37670             
37671             this.editicon = this.wrap.createChild(
37672                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37673             if (this.addicon) {
37674                 this.editicon.setStyle('margin-left', '40px');
37675             }
37676             this.editicon.on('click', function(e) {
37677                 
37678                 // we fire even  if inothing is selected..
37679                 this.fireEvent('edit', this, this.lastData );
37680                 
37681             }, this);
37682         }
37683         
37684         
37685         
37686     },
37687
37688     // private
37689     initEvents : function(){
37690         Roo.form.ComboBox.superclass.initEvents.call(this);
37691
37692         this.keyNav = new Roo.KeyNav(this.el, {
37693             "up" : function(e){
37694                 this.inKeyMode = true;
37695                 this.selectPrev();
37696             },
37697
37698             "down" : function(e){
37699                 if(!this.isExpanded()){
37700                     this.onTriggerClick();
37701                 }else{
37702                     this.inKeyMode = true;
37703                     this.selectNext();
37704                 }
37705             },
37706
37707             "enter" : function(e){
37708                 this.onViewClick();
37709                 //return true;
37710             },
37711
37712             "esc" : function(e){
37713                 this.collapse();
37714             },
37715
37716             "tab" : function(e){
37717                 this.onViewClick(false);
37718                 this.fireEvent("specialkey", this, e);
37719                 return true;
37720             },
37721
37722             scope : this,
37723
37724             doRelay : function(foo, bar, hname){
37725                 if(hname == 'down' || this.scope.isExpanded()){
37726                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37727                 }
37728                 return true;
37729             },
37730
37731             forceKeyDown: true
37732         });
37733         this.queryDelay = Math.max(this.queryDelay || 10,
37734                 this.mode == 'local' ? 10 : 250);
37735         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37736         if(this.typeAhead){
37737             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37738         }
37739         if(this.editable !== false){
37740             this.el.on("keyup", this.onKeyUp, this);
37741         }
37742         if(this.forceSelection){
37743             this.on('blur', this.doForce, this);
37744         }
37745     },
37746
37747     onDestroy : function(){
37748         if(this.view){
37749             this.view.setStore(null);
37750             this.view.el.removeAllListeners();
37751             this.view.el.remove();
37752             this.view.purgeListeners();
37753         }
37754         if(this.list){
37755             this.list.destroy();
37756         }
37757         if(this.store){
37758             this.store.un('beforeload', this.onBeforeLoad, this);
37759             this.store.un('load', this.onLoad, this);
37760             this.store.un('loadexception', this.collapse, this);
37761         }
37762         Roo.form.ComboBox.superclass.onDestroy.call(this);
37763     },
37764
37765     // private
37766     fireKey : function(e){
37767         if(e.isNavKeyPress() && !this.list.isVisible()){
37768             this.fireEvent("specialkey", this, e);
37769         }
37770     },
37771
37772     // private
37773     onResize: function(w, h){
37774         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37775         
37776         if(typeof w != 'number'){
37777             // we do not handle it!?!?
37778             return;
37779         }
37780         var tw = this.trigger.getWidth();
37781         tw += this.addicon ? this.addicon.getWidth() : 0;
37782         tw += this.editicon ? this.editicon.getWidth() : 0;
37783         var x = w - tw;
37784         this.el.setWidth( this.adjustWidth('input', x));
37785             
37786         this.trigger.setStyle('left', x+'px');
37787         
37788         if(this.list && this.listWidth === undefined){
37789             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37790             this.list.setWidth(lw);
37791             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37792         }
37793         
37794     
37795         
37796     },
37797
37798     /**
37799      * Allow or prevent the user from directly editing the field text.  If false is passed,
37800      * the user will only be able to select from the items defined in the dropdown list.  This method
37801      * is the runtime equivalent of setting the 'editable' config option at config time.
37802      * @param {Boolean} value True to allow the user to directly edit the field text
37803      */
37804     setEditable : function(value){
37805         if(value == this.editable){
37806             return;
37807         }
37808         this.editable = value;
37809         if(!value){
37810             this.el.dom.setAttribute('readOnly', true);
37811             this.el.on('mousedown', this.onTriggerClick,  this);
37812             this.el.addClass('x-combo-noedit');
37813         }else{
37814             this.el.dom.setAttribute('readOnly', false);
37815             this.el.un('mousedown', this.onTriggerClick,  this);
37816             this.el.removeClass('x-combo-noedit');
37817         }
37818     },
37819
37820     // private
37821     onBeforeLoad : function(){
37822         if(!this.hasFocus){
37823             return;
37824         }
37825         this.innerList.update(this.loadingText ?
37826                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37827         this.restrictHeight();
37828         this.selectedIndex = -1;
37829     },
37830
37831     // private
37832     onLoad : function(){
37833         if(!this.hasFocus){
37834             return;
37835         }
37836         if(this.store.getCount() > 0){
37837             this.expand();
37838             this.restrictHeight();
37839             if(this.lastQuery == this.allQuery){
37840                 if(this.editable){
37841                     this.el.dom.select();
37842                 }
37843                 if(!this.selectByValue(this.value, true)){
37844                     this.select(0, true);
37845                 }
37846             }else{
37847                 this.selectNext();
37848                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37849                     this.taTask.delay(this.typeAheadDelay);
37850                 }
37851             }
37852         }else{
37853             this.onEmptyResults();
37854         }
37855         //this.el.focus();
37856     },
37857
37858     // private
37859     onTypeAhead : function(){
37860         if(this.store.getCount() > 0){
37861             var r = this.store.getAt(0);
37862             var newValue = r.data[this.displayField];
37863             var len = newValue.length;
37864             var selStart = this.getRawValue().length;
37865             if(selStart != len){
37866                 this.setRawValue(newValue);
37867                 this.selectText(selStart, newValue.length);
37868             }
37869         }
37870     },
37871
37872     // private
37873     onSelect : function(record, index){
37874         if(this.fireEvent('beforeselect', this, record, index) !== false){
37875             this.setFromData(index > -1 ? record.data : false);
37876             this.collapse();
37877             this.fireEvent('select', this, record, index);
37878         }
37879     },
37880
37881     /**
37882      * Returns the currently selected field value or empty string if no value is set.
37883      * @return {String} value The selected value
37884      */
37885     getValue : function(){
37886         if(this.valueField){
37887             return typeof this.value != 'undefined' ? this.value : '';
37888         }else{
37889             return Roo.form.ComboBox.superclass.getValue.call(this);
37890         }
37891     },
37892
37893     /**
37894      * Clears any text/value currently set in the field
37895      */
37896     clearValue : function(){
37897         if(this.hiddenField){
37898             this.hiddenField.value = '';
37899         }
37900         this.value = '';
37901         this.setRawValue('');
37902         this.lastSelectionText = '';
37903         this.applyEmptyText();
37904     },
37905
37906     /**
37907      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37908      * will be displayed in the field.  If the value does not match the data value of an existing item,
37909      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37910      * Otherwise the field will be blank (although the value will still be set).
37911      * @param {String} value The value to match
37912      */
37913     setValue : function(v){
37914         var text = v;
37915         if(this.valueField){
37916             var r = this.findRecord(this.valueField, v);
37917             if(r){
37918                 text = r.data[this.displayField];
37919             }else if(this.valueNotFoundText !== undefined){
37920                 text = this.valueNotFoundText;
37921             }
37922         }
37923         this.lastSelectionText = text;
37924         if(this.hiddenField){
37925             this.hiddenField.value = v;
37926         }
37927         Roo.form.ComboBox.superclass.setValue.call(this, text);
37928         this.value = v;
37929     },
37930     /**
37931      * @property {Object} the last set data for the element
37932      */
37933     
37934     lastData : false,
37935     /**
37936      * Sets the value of the field based on a object which is related to the record format for the store.
37937      * @param {Object} value the value to set as. or false on reset?
37938      */
37939     setFromData : function(o){
37940         var dv = ''; // display value
37941         var vv = ''; // value value..
37942         this.lastData = o;
37943         if (this.displayField) {
37944             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37945         } else {
37946             // this is an error condition!!!
37947             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37948         }
37949         
37950         if(this.valueField){
37951             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37952         }
37953         if(this.hiddenField){
37954             this.hiddenField.value = vv;
37955             
37956             this.lastSelectionText = dv;
37957             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37958             this.value = vv;
37959             return;
37960         }
37961         // no hidden field.. - we store the value in 'value', but still display
37962         // display field!!!!
37963         this.lastSelectionText = dv;
37964         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37965         this.value = vv;
37966         
37967         
37968     },
37969     // private
37970     reset : function(){
37971         // overridden so that last data is reset..
37972         this.setValue(this.originalValue);
37973         this.clearInvalid();
37974         this.lastData = false;
37975     },
37976     // private
37977     findRecord : function(prop, value){
37978         var record;
37979         if(this.store.getCount() > 0){
37980             this.store.each(function(r){
37981                 if(r.data[prop] == value){
37982                     record = r;
37983                     return false;
37984                 }
37985                 return true;
37986             });
37987         }
37988         return record;
37989     },
37990     
37991     getName: function()
37992     {
37993         // returns hidden if it's set..
37994         if (!this.rendered) {return ''};
37995         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37996         
37997     },
37998     // private
37999     onViewMove : function(e, t){
38000         this.inKeyMode = false;
38001     },
38002
38003     // private
38004     onViewOver : function(e, t){
38005         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38006             return;
38007         }
38008         var item = this.view.findItemFromChild(t);
38009         if(item){
38010             var index = this.view.indexOf(item);
38011             this.select(index, false);
38012         }
38013     },
38014
38015     // private
38016     onViewClick : function(doFocus)
38017     {
38018         var index = this.view.getSelectedIndexes()[0];
38019         var r = this.store.getAt(index);
38020         if(r){
38021             this.onSelect(r, index);
38022         }
38023         if(doFocus !== false && !this.blockFocus){
38024             this.el.focus();
38025         }
38026     },
38027
38028     // private
38029     restrictHeight : function(){
38030         this.innerList.dom.style.height = '';
38031         var inner = this.innerList.dom;
38032         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38033         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38034         this.list.beginUpdate();
38035         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38036         this.list.alignTo(this.el, this.listAlign);
38037         this.list.endUpdate();
38038     },
38039
38040     // private
38041     onEmptyResults : function(){
38042         this.collapse();
38043     },
38044
38045     /**
38046      * Returns true if the dropdown list is expanded, else false.
38047      */
38048     isExpanded : function(){
38049         return this.list.isVisible();
38050     },
38051
38052     /**
38053      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38054      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38055      * @param {String} value The data value of the item to select
38056      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38057      * selected item if it is not currently in view (defaults to true)
38058      * @return {Boolean} True if the value matched an item in the list, else false
38059      */
38060     selectByValue : function(v, scrollIntoView){
38061         if(v !== undefined && v !== null){
38062             var r = this.findRecord(this.valueField || this.displayField, v);
38063             if(r){
38064                 this.select(this.store.indexOf(r), scrollIntoView);
38065                 return true;
38066             }
38067         }
38068         return false;
38069     },
38070
38071     /**
38072      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38073      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38074      * @param {Number} index The zero-based index of the list item to select
38075      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38076      * selected item if it is not currently in view (defaults to true)
38077      */
38078     select : function(index, scrollIntoView){
38079         this.selectedIndex = index;
38080         this.view.select(index);
38081         if(scrollIntoView !== false){
38082             var el = this.view.getNode(index);
38083             if(el){
38084                 this.innerList.scrollChildIntoView(el, false);
38085             }
38086         }
38087     },
38088
38089     // private
38090     selectNext : function(){
38091         var ct = this.store.getCount();
38092         if(ct > 0){
38093             if(this.selectedIndex == -1){
38094                 this.select(0);
38095             }else if(this.selectedIndex < ct-1){
38096                 this.select(this.selectedIndex+1);
38097             }
38098         }
38099     },
38100
38101     // private
38102     selectPrev : function(){
38103         var ct = this.store.getCount();
38104         if(ct > 0){
38105             if(this.selectedIndex == -1){
38106                 this.select(0);
38107             }else if(this.selectedIndex != 0){
38108                 this.select(this.selectedIndex-1);
38109             }
38110         }
38111     },
38112
38113     // private
38114     onKeyUp : function(e){
38115         if(this.editable !== false && !e.isSpecialKey()){
38116             this.lastKey = e.getKey();
38117             this.dqTask.delay(this.queryDelay);
38118         }
38119     },
38120
38121     // private
38122     validateBlur : function(){
38123         return !this.list || !this.list.isVisible();   
38124     },
38125
38126     // private
38127     initQuery : function(){
38128         this.doQuery(this.getRawValue());
38129     },
38130
38131     // private
38132     doForce : function(){
38133         if(this.el.dom.value.length > 0){
38134             this.el.dom.value =
38135                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38136             this.applyEmptyText();
38137         }
38138     },
38139
38140     /**
38141      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38142      * query allowing the query action to be canceled if needed.
38143      * @param {String} query The SQL query to execute
38144      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38145      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38146      * saved in the current store (defaults to false)
38147      */
38148     doQuery : function(q, forceAll){
38149         if(q === undefined || q === null){
38150             q = '';
38151         }
38152         var qe = {
38153             query: q,
38154             forceAll: forceAll,
38155             combo: this,
38156             cancel:false
38157         };
38158         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38159             return false;
38160         }
38161         q = qe.query;
38162         forceAll = qe.forceAll;
38163         if(forceAll === true || (q.length >= this.minChars)){
38164             if(this.lastQuery != q || this.alwaysQuery){
38165                 this.lastQuery = q;
38166                 if(this.mode == 'local'){
38167                     this.selectedIndex = -1;
38168                     if(forceAll){
38169                         this.store.clearFilter();
38170                     }else{
38171                         this.store.filter(this.displayField, q);
38172                     }
38173                     this.onLoad();
38174                 }else{
38175                     this.store.baseParams[this.queryParam] = q;
38176                     this.store.load({
38177                         params: this.getParams(q)
38178                     });
38179                     this.expand();
38180                 }
38181             }else{
38182                 this.selectedIndex = -1;
38183                 this.onLoad();   
38184             }
38185         }
38186     },
38187
38188     // private
38189     getParams : function(q){
38190         var p = {};
38191         //p[this.queryParam] = q;
38192         if(this.pageSize){
38193             p.start = 0;
38194             p.limit = this.pageSize;
38195         }
38196         return p;
38197     },
38198
38199     /**
38200      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38201      */
38202     collapse : function(){
38203         if(!this.isExpanded()){
38204             return;
38205         }
38206         this.list.hide();
38207         Roo.get(document).un('mousedown', this.collapseIf, this);
38208         Roo.get(document).un('mousewheel', this.collapseIf, this);
38209         if (!this.editable) {
38210             Roo.get(document).un('keydown', this.listKeyPress, this);
38211         }
38212         this.fireEvent('collapse', this);
38213     },
38214
38215     // private
38216     collapseIf : function(e){
38217         if(!e.within(this.wrap) && !e.within(this.list)){
38218             this.collapse();
38219         }
38220     },
38221
38222     /**
38223      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38224      */
38225     expand : function(){
38226         if(this.isExpanded() || !this.hasFocus){
38227             return;
38228         }
38229         this.list.alignTo(this.el, this.listAlign);
38230         this.list.show();
38231         Roo.get(document).on('mousedown', this.collapseIf, this);
38232         Roo.get(document).on('mousewheel', this.collapseIf, this);
38233         if (!this.editable) {
38234             Roo.get(document).on('keydown', this.listKeyPress, this);
38235         }
38236         
38237         this.fireEvent('expand', this);
38238     },
38239
38240     // private
38241     // Implements the default empty TriggerField.onTriggerClick function
38242     onTriggerClick : function(){
38243         if(this.disabled){
38244             return;
38245         }
38246         if(this.isExpanded()){
38247             this.collapse();
38248             if (!this.blockFocus) {
38249                 this.el.focus();
38250             }
38251             
38252         }else {
38253             this.hasFocus = true;
38254             if(this.triggerAction == 'all') {
38255                 this.doQuery(this.allQuery, true);
38256             } else {
38257                 this.doQuery(this.getRawValue());
38258             }
38259             if (!this.blockFocus) {
38260                 this.el.focus();
38261             }
38262         }
38263     },
38264     listKeyPress : function(e)
38265     {
38266         //Roo.log('listkeypress');
38267         // scroll to first matching element based on key pres..
38268         if (e.isSpecialKey()) {
38269             return false;
38270         }
38271         var k = String.fromCharCode(e.getKey()).toUpperCase();
38272         //Roo.log(k);
38273         var match  = false;
38274         var csel = this.view.getSelectedNodes();
38275         var cselitem = false;
38276         if (csel.length) {
38277             var ix = this.view.indexOf(csel[0]);
38278             cselitem  = this.store.getAt(ix);
38279             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38280                 cselitem = false;
38281             }
38282             
38283         }
38284         
38285         this.store.each(function(v) { 
38286             if (cselitem) {
38287                 // start at existing selection.
38288                 if (cselitem.id == v.id) {
38289                     cselitem = false;
38290                 }
38291                 return;
38292             }
38293                 
38294             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38295                 match = this.store.indexOf(v);
38296                 return false;
38297             }
38298         }, this);
38299         
38300         if (match === false) {
38301             return true; // no more action?
38302         }
38303         // scroll to?
38304         this.view.select(match);
38305         var sn = Roo.get(this.view.getSelectedNodes()[0])
38306         sn.scrollIntoView(sn.dom.parentNode, false);
38307     }
38308
38309     /** 
38310     * @cfg {Boolean} grow 
38311     * @hide 
38312     */
38313     /** 
38314     * @cfg {Number} growMin 
38315     * @hide 
38316     */
38317     /** 
38318     * @cfg {Number} growMax 
38319     * @hide 
38320     */
38321     /**
38322      * @hide
38323      * @method autoSize
38324      */
38325 });/*
38326  * Based on:
38327  * Ext JS Library 1.1.1
38328  * Copyright(c) 2006-2007, Ext JS, LLC.
38329  *
38330  * Originally Released Under LGPL - original licence link has changed is not relivant.
38331  *
38332  * Fork - LGPL
38333  * <script type="text/javascript">
38334  */
38335 /**
38336  * @class Roo.form.Checkbox
38337  * @extends Roo.form.Field
38338  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38339  * @constructor
38340  * Creates a new Checkbox
38341  * @param {Object} config Configuration options
38342  */
38343 Roo.form.Checkbox = function(config){
38344     Roo.form.Checkbox.superclass.constructor.call(this, config);
38345     this.addEvents({
38346         /**
38347          * @event check
38348          * Fires when the checkbox is checked or unchecked.
38349              * @param {Roo.form.Checkbox} this This checkbox
38350              * @param {Boolean} checked The new checked value
38351              */
38352         check : true
38353     });
38354 };
38355
38356 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38357     /**
38358      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38359      */
38360     focusClass : undefined,
38361     /**
38362      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38363      */
38364     fieldClass: "x-form-field",
38365     /**
38366      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38367      */
38368     checked: false,
38369     /**
38370      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38371      * {tag: "input", type: "checkbox", autocomplete: "off"})
38372      */
38373     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38374     /**
38375      * @cfg {String} boxLabel The text that appears beside the checkbox
38376      */
38377     boxLabel : "",
38378     /**
38379      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38380      */  
38381     inputValue : '1',
38382     /**
38383      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38384      */
38385      valueOff: '0', // value when not checked..
38386
38387     actionMode : 'viewEl', 
38388     //
38389     // private
38390     itemCls : 'x-menu-check-item x-form-item',
38391     groupClass : 'x-menu-group-item',
38392     inputType : 'hidden',
38393     
38394     
38395     inSetChecked: false, // check that we are not calling self...
38396     
38397     inputElement: false, // real input element?
38398     basedOn: false, // ????
38399     
38400     isFormField: true, // not sure where this is needed!!!!
38401
38402     onResize : function(){
38403         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38404         if(!this.boxLabel){
38405             this.el.alignTo(this.wrap, 'c-c');
38406         }
38407     },
38408
38409     initEvents : function(){
38410         Roo.form.Checkbox.superclass.initEvents.call(this);
38411         this.el.on("click", this.onClick,  this);
38412         this.el.on("change", this.onClick,  this);
38413     },
38414
38415
38416     getResizeEl : function(){
38417         return this.wrap;
38418     },
38419
38420     getPositionEl : function(){
38421         return this.wrap;
38422     },
38423
38424     // private
38425     onRender : function(ct, position){
38426         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38427         /*
38428         if(this.inputValue !== undefined){
38429             this.el.dom.value = this.inputValue;
38430         }
38431         */
38432         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38433         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38434         var viewEl = this.wrap.createChild({ 
38435             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38436         this.viewEl = viewEl;   
38437         this.wrap.on('click', this.onClick,  this); 
38438         
38439         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38440         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38441         
38442         
38443         
38444         if(this.boxLabel){
38445             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38446         //    viewEl.on('click', this.onClick,  this); 
38447         }
38448         //if(this.checked){
38449             this.setChecked(this.checked);
38450         //}else{
38451             //this.checked = this.el.dom;
38452         //}
38453
38454     },
38455
38456     // private
38457     initValue : Roo.emptyFn,
38458
38459     /**
38460      * Returns the checked state of the checkbox.
38461      * @return {Boolean} True if checked, else false
38462      */
38463     getValue : function(){
38464         if(this.el){
38465             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38466         }
38467         return this.valueOff;
38468         
38469     },
38470
38471         // private
38472     onClick : function(){ 
38473         this.setChecked(!this.checked);
38474
38475         //if(this.el.dom.checked != this.checked){
38476         //    this.setValue(this.el.dom.checked);
38477        // }
38478     },
38479
38480     /**
38481      * Sets the checked state of the checkbox.
38482      * On is always based on a string comparison between inputValue and the param.
38483      * @param {Boolean/String} value - the value to set 
38484      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38485      */
38486     setValue : function(v,suppressEvent){
38487         
38488         
38489         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38490         //if(this.el && this.el.dom){
38491         //    this.el.dom.checked = this.checked;
38492         //    this.el.dom.defaultChecked = this.checked;
38493         //}
38494         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38495         //this.fireEvent("check", this, this.checked);
38496     },
38497     // private..
38498     setChecked : function(state,suppressEvent)
38499     {
38500         if (this.inSetChecked) {
38501             this.checked = state;
38502             return;
38503         }
38504         
38505     
38506         if(this.wrap){
38507             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38508         }
38509         this.checked = state;
38510         if(suppressEvent !== true){
38511             this.fireEvent('check', this, state);
38512         }
38513         this.inSetChecked = true;
38514         this.el.dom.value = state ? this.inputValue : this.valueOff;
38515         this.inSetChecked = false;
38516         
38517     },
38518     // handle setting of hidden value by some other method!!?!?
38519     setFromHidden: function()
38520     {
38521         if(!this.el){
38522             return;
38523         }
38524         //console.log("SET FROM HIDDEN");
38525         //alert('setFrom hidden');
38526         this.setValue(this.el.dom.value);
38527     },
38528     
38529     onDestroy : function()
38530     {
38531         if(this.viewEl){
38532             Roo.get(this.viewEl).remove();
38533         }
38534          
38535         Roo.form.Checkbox.superclass.onDestroy.call(this);
38536     }
38537
38538 });/*
38539  * Based on:
38540  * Ext JS Library 1.1.1
38541  * Copyright(c) 2006-2007, Ext JS, LLC.
38542  *
38543  * Originally Released Under LGPL - original licence link has changed is not relivant.
38544  *
38545  * Fork - LGPL
38546  * <script type="text/javascript">
38547  */
38548  
38549 /**
38550  * @class Roo.form.Radio
38551  * @extends Roo.form.Checkbox
38552  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38553  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38554  * @constructor
38555  * Creates a new Radio
38556  * @param {Object} config Configuration options
38557  */
38558 Roo.form.Radio = function(){
38559     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38560 };
38561 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38562     inputType: 'radio',
38563
38564     /**
38565      * If this radio is part of a group, it will return the selected value
38566      * @return {String}
38567      */
38568     getGroupValue : function(){
38569         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38570     }
38571 });//<script type="text/javascript">
38572
38573 /*
38574  * Ext JS Library 1.1.1
38575  * Copyright(c) 2006-2007, Ext JS, LLC.
38576  * licensing@extjs.com
38577  * 
38578  * http://www.extjs.com/license
38579  */
38580  
38581  /*
38582   * 
38583   * Known bugs:
38584   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38585   * - IE ? - no idea how much works there.
38586   * 
38587   * 
38588   * 
38589   */
38590  
38591
38592 /**
38593  * @class Ext.form.HtmlEditor
38594  * @extends Ext.form.Field
38595  * Provides a lightweight HTML Editor component.
38596  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38597  * 
38598  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38599  * supported by this editor.</b><br/><br/>
38600  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38601  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38602  */
38603 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38604       /**
38605      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38606      */
38607     toolbars : false,
38608     /**
38609      * @cfg {String} createLinkText The default text for the create link prompt
38610      */
38611     createLinkText : 'Please enter the URL for the link:',
38612     /**
38613      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38614      */
38615     defaultLinkValue : 'http:/'+'/',
38616    
38617      /**
38618      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38619      *                        Roo.resizable.
38620      */
38621     resizable : false,
38622      /**
38623      * @cfg {Number} height (in pixels)
38624      */   
38625     height: 300,
38626    /**
38627      * @cfg {Number} width (in pixels)
38628      */   
38629     width: 500,
38630     
38631     /**
38632      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38633      * 
38634      */
38635     stylesheets: false,
38636     
38637     // id of frame..
38638     frameId: false,
38639     
38640     // private properties
38641     validationEvent : false,
38642     deferHeight: true,
38643     initialized : false,
38644     activated : false,
38645     sourceEditMode : false,
38646     onFocus : Roo.emptyFn,
38647     iframePad:3,
38648     hideMode:'offsets',
38649     
38650     defaultAutoCreate : { // modified by initCompnoent..
38651         tag: "textarea",
38652         style:"width:500px;height:300px;",
38653         autocomplete: "off"
38654     },
38655
38656     // private
38657     initComponent : function(){
38658         this.addEvents({
38659             /**
38660              * @event initialize
38661              * Fires when the editor is fully initialized (including the iframe)
38662              * @param {HtmlEditor} this
38663              */
38664             initialize: true,
38665             /**
38666              * @event activate
38667              * Fires when the editor is first receives the focus. Any insertion must wait
38668              * until after this event.
38669              * @param {HtmlEditor} this
38670              */
38671             activate: true,
38672              /**
38673              * @event beforesync
38674              * Fires before the textarea is updated with content from the editor iframe. Return false
38675              * to cancel the sync.
38676              * @param {HtmlEditor} this
38677              * @param {String} html
38678              */
38679             beforesync: true,
38680              /**
38681              * @event beforepush
38682              * Fires before the iframe editor is updated with content from the textarea. Return false
38683              * to cancel the push.
38684              * @param {HtmlEditor} this
38685              * @param {String} html
38686              */
38687             beforepush: true,
38688              /**
38689              * @event sync
38690              * Fires when the textarea is updated with content from the editor iframe.
38691              * @param {HtmlEditor} this
38692              * @param {String} html
38693              */
38694             sync: true,
38695              /**
38696              * @event push
38697              * Fires when the iframe editor is updated with content from the textarea.
38698              * @param {HtmlEditor} this
38699              * @param {String} html
38700              */
38701             push: true,
38702              /**
38703              * @event editmodechange
38704              * Fires when the editor switches edit modes
38705              * @param {HtmlEditor} this
38706              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38707              */
38708             editmodechange: true,
38709             /**
38710              * @event editorevent
38711              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38712              * @param {HtmlEditor} this
38713              */
38714             editorevent: true
38715         });
38716         this.defaultAutoCreate =  {
38717             tag: "textarea",
38718             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38719             autocomplete: "off"
38720         };
38721     },
38722
38723     /**
38724      * Protected method that will not generally be called directly. It
38725      * is called when the editor creates its toolbar. Override this method if you need to
38726      * add custom toolbar buttons.
38727      * @param {HtmlEditor} editor
38728      */
38729     createToolbar : function(editor){
38730         if (!editor.toolbars || !editor.toolbars.length) {
38731             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38732         }
38733         
38734         for (var i =0 ; i < editor.toolbars.length;i++) {
38735             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38736             editor.toolbars[i].init(editor);
38737         }
38738          
38739         
38740     },
38741
38742     /**
38743      * Protected method that will not generally be called directly. It
38744      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38745      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38746      */
38747     getDocMarkup : function(){
38748         // body styles..
38749         var st = '';
38750         if (this.stylesheets === false) {
38751             
38752             Roo.get(document.head).select('style').each(function(node) {
38753                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38754             });
38755             
38756             Roo.get(document.head).select('link').each(function(node) { 
38757                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38758             });
38759             
38760         } else if (!this.stylesheets.length) {
38761                 // simple..
38762                 st = '<style type="text/css">' +
38763                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38764                    '</style>';
38765         } else {
38766             Roo.each(this.stylesheets, function(s) {
38767                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38768             });
38769             
38770         }
38771         
38772         return '<html><head>' + st  +
38773             //<style type="text/css">' +
38774             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38775             //'</style>' +
38776             ' </head><body></body></html>';
38777     },
38778
38779     // private
38780     onRender : function(ct, position)
38781     {
38782         var _t = this;
38783         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38784         this.el.dom.style.border = '0 none';
38785         this.el.dom.setAttribute('tabIndex', -1);
38786         this.el.addClass('x-hidden');
38787         if(Roo.isIE){ // fix IE 1px bogus margin
38788             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38789         }
38790         this.wrap = this.el.wrap({
38791             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38792         });
38793         
38794         if (this.resizable) {
38795             this.resizeEl = new Roo.Resizable(this.wrap, {
38796                 pinned : true,
38797                 wrap: true,
38798                 dynamic : true,
38799                 minHeight : this.height,
38800                 height: this.height,
38801                 handles : this.resizable,
38802                 width: this.width,
38803                 listeners : {
38804                     resize : function(r, w, h) {
38805                         _t.onResize(w,h); // -something
38806                     }
38807                 }
38808             });
38809             
38810         }
38811
38812         this.frameId = Roo.id();
38813         
38814         this.createToolbar(this);
38815         
38816       
38817         
38818         var iframe = this.wrap.createChild({
38819             tag: 'iframe',
38820             id: this.frameId,
38821             name: this.frameId,
38822             frameBorder : 'no',
38823             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38824         }, this.el
38825         );
38826         
38827        // console.log(iframe);
38828         //this.wrap.dom.appendChild(iframe);
38829
38830         this.iframe = iframe.dom;
38831
38832          this.assignDocWin();
38833         
38834         this.doc.designMode = 'on';
38835        
38836         this.doc.open();
38837         this.doc.write(this.getDocMarkup());
38838         this.doc.close();
38839
38840         
38841         var task = { // must defer to wait for browser to be ready
38842             run : function(){
38843                 //console.log("run task?" + this.doc.readyState);
38844                 this.assignDocWin();
38845                 if(this.doc.body || this.doc.readyState == 'complete'){
38846                     try {
38847                         this.doc.designMode="on";
38848                     } catch (e) {
38849                         return;
38850                     }
38851                     Roo.TaskMgr.stop(task);
38852                     this.initEditor.defer(10, this);
38853                 }
38854             },
38855             interval : 10,
38856             duration:10000,
38857             scope: this
38858         };
38859         Roo.TaskMgr.start(task);
38860
38861         if(!this.width){
38862             this.setSize(this.wrap.getSize());
38863         }
38864         if (this.resizeEl) {
38865             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38866             // should trigger onReize..
38867         }
38868     },
38869
38870     // private
38871     onResize : function(w, h)
38872     {
38873         //Roo.log('resize: ' +w + ',' + h );
38874         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38875         if(this.el && this.iframe){
38876             if(typeof w == 'number'){
38877                 var aw = w - this.wrap.getFrameWidth('lr');
38878                 this.el.setWidth(this.adjustWidth('textarea', aw));
38879                 this.iframe.style.width = aw + 'px';
38880             }
38881             if(typeof h == 'number'){
38882                 var tbh = 0;
38883                 for (var i =0; i < this.toolbars.length;i++) {
38884                     // fixme - ask toolbars for heights?
38885                     tbh += this.toolbars[i].tb.el.getHeight();
38886                     if (this.toolbars[i].footer) {
38887                         tbh += this.toolbars[i].footer.el.getHeight();
38888                     }
38889                 }
38890                 
38891                 
38892                 
38893                 
38894                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38895                 ah -= 5; // knock a few pixes off for look..
38896                 this.el.setHeight(this.adjustWidth('textarea', ah));
38897                 this.iframe.style.height = ah + 'px';
38898                 if(this.doc){
38899                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38900                 }
38901             }
38902         }
38903     },
38904
38905     /**
38906      * Toggles the editor between standard and source edit mode.
38907      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38908      */
38909     toggleSourceEdit : function(sourceEditMode){
38910         
38911         this.sourceEditMode = sourceEditMode === true;
38912         
38913         if(this.sourceEditMode){
38914           
38915             this.syncValue();
38916             this.iframe.className = 'x-hidden';
38917             this.el.removeClass('x-hidden');
38918             this.el.dom.removeAttribute('tabIndex');
38919             this.el.focus();
38920         }else{
38921              
38922             this.pushValue();
38923             this.iframe.className = '';
38924             this.el.addClass('x-hidden');
38925             this.el.dom.setAttribute('tabIndex', -1);
38926             this.deferFocus();
38927         }
38928         this.setSize(this.wrap.getSize());
38929         this.fireEvent('editmodechange', this, this.sourceEditMode);
38930     },
38931
38932     // private used internally
38933     createLink : function(){
38934         var url = prompt(this.createLinkText, this.defaultLinkValue);
38935         if(url && url != 'http:/'+'/'){
38936             this.relayCmd('createlink', url);
38937         }
38938     },
38939
38940     // private (for BoxComponent)
38941     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38942
38943     // private (for BoxComponent)
38944     getResizeEl : function(){
38945         return this.wrap;
38946     },
38947
38948     // private (for BoxComponent)
38949     getPositionEl : function(){
38950         return this.wrap;
38951     },
38952
38953     // private
38954     initEvents : function(){
38955         this.originalValue = this.getValue();
38956     },
38957
38958     /**
38959      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38960      * @method
38961      */
38962     markInvalid : Roo.emptyFn,
38963     /**
38964      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38965      * @method
38966      */
38967     clearInvalid : Roo.emptyFn,
38968
38969     setValue : function(v){
38970         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38971         this.pushValue();
38972     },
38973
38974     /**
38975      * Protected method that will not generally be called directly. If you need/want
38976      * custom HTML cleanup, this is the method you should override.
38977      * @param {String} html The HTML to be cleaned
38978      * return {String} The cleaned HTML
38979      */
38980     cleanHtml : function(html){
38981         html = String(html);
38982         if(html.length > 5){
38983             if(Roo.isSafari){ // strip safari nonsense
38984                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38985             }
38986         }
38987         if(html == '&nbsp;'){
38988             html = '';
38989         }
38990         return html;
38991     },
38992
38993     /**
38994      * Protected method that will not generally be called directly. Syncs the contents
38995      * of the editor iframe with the textarea.
38996      */
38997     syncValue : function(){
38998         if(this.initialized){
38999             var bd = (this.doc.body || this.doc.documentElement);
39000             this.cleanUpPaste();
39001             var html = bd.innerHTML;
39002             if(Roo.isSafari){
39003                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
39004                 var m = bs.match(/text-align:(.*?);/i);
39005                 if(m && m[1]){
39006                     html = '<div style="'+m[0]+'">' + html + '</div>';
39007                 }
39008             }
39009             html = this.cleanHtml(html);
39010             if(this.fireEvent('beforesync', this, html) !== false){
39011                 this.el.dom.value = html;
39012                 this.fireEvent('sync', this, html);
39013             }
39014         }
39015     },
39016
39017     /**
39018      * Protected method that will not generally be called directly. Pushes the value of the textarea
39019      * into the iframe editor.
39020      */
39021     pushValue : function(){
39022         if(this.initialized){
39023             var v = this.el.dom.value;
39024             if(v.length < 1){
39025                 v = '&#160;';
39026             }
39027             
39028             if(this.fireEvent('beforepush', this, v) !== false){
39029                 var d = (this.doc.body || this.doc.documentElement);
39030                 d.innerHTML = v;
39031                 this.cleanUpPaste();
39032                 this.el.dom.value = d.innerHTML;
39033                 this.fireEvent('push', this, v);
39034             }
39035         }
39036     },
39037
39038     // private
39039     deferFocus : function(){
39040         this.focus.defer(10, this);
39041     },
39042
39043     // doc'ed in Field
39044     focus : function(){
39045         if(this.win && !this.sourceEditMode){
39046             this.win.focus();
39047         }else{
39048             this.el.focus();
39049         }
39050     },
39051     
39052     assignDocWin: function()
39053     {
39054         var iframe = this.iframe;
39055         
39056          if(Roo.isIE){
39057             this.doc = iframe.contentWindow.document;
39058             this.win = iframe.contentWindow;
39059         } else {
39060             if (!Roo.get(this.frameId)) {
39061                 return;
39062             }
39063             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39064             this.win = Roo.get(this.frameId).dom.contentWindow;
39065         }
39066     },
39067     
39068     // private
39069     initEditor : function(){
39070         //console.log("INIT EDITOR");
39071         this.assignDocWin();
39072         
39073         
39074         
39075         this.doc.designMode="on";
39076         this.doc.open();
39077         this.doc.write(this.getDocMarkup());
39078         this.doc.close();
39079         
39080         var dbody = (this.doc.body || this.doc.documentElement);
39081         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39082         // this copies styles from the containing element into thsi one..
39083         // not sure why we need all of this..
39084         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39085         ss['background-attachment'] = 'fixed'; // w3c
39086         dbody.bgProperties = 'fixed'; // ie
39087         Roo.DomHelper.applyStyles(dbody, ss);
39088         Roo.EventManager.on(this.doc, {
39089             //'mousedown': this.onEditorEvent,
39090             'mouseup': this.onEditorEvent,
39091             'dblclick': this.onEditorEvent,
39092             'click': this.onEditorEvent,
39093             'keyup': this.onEditorEvent,
39094             buffer:100,
39095             scope: this
39096         });
39097         if(Roo.isGecko){
39098             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39099         }
39100         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39101             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39102         }
39103         this.initialized = true;
39104
39105         this.fireEvent('initialize', this);
39106         this.pushValue();
39107     },
39108
39109     // private
39110     onDestroy : function(){
39111         
39112         
39113         
39114         if(this.rendered){
39115             
39116             for (var i =0; i < this.toolbars.length;i++) {
39117                 // fixme - ask toolbars for heights?
39118                 this.toolbars[i].onDestroy();
39119             }
39120             
39121             this.wrap.dom.innerHTML = '';
39122             this.wrap.remove();
39123         }
39124     },
39125
39126     // private
39127     onFirstFocus : function(){
39128         
39129         this.assignDocWin();
39130         
39131         
39132         this.activated = true;
39133         for (var i =0; i < this.toolbars.length;i++) {
39134             this.toolbars[i].onFirstFocus();
39135         }
39136        
39137         if(Roo.isGecko){ // prevent silly gecko errors
39138             this.win.focus();
39139             var s = this.win.getSelection();
39140             if(!s.focusNode || s.focusNode.nodeType != 3){
39141                 var r = s.getRangeAt(0);
39142                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39143                 r.collapse(true);
39144                 this.deferFocus();
39145             }
39146             try{
39147                 this.execCmd('useCSS', true);
39148                 this.execCmd('styleWithCSS', false);
39149             }catch(e){}
39150         }
39151         this.fireEvent('activate', this);
39152     },
39153
39154     // private
39155     adjustFont: function(btn){
39156         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39157         //if(Roo.isSafari){ // safari
39158         //    adjust *= 2;
39159        // }
39160         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39161         if(Roo.isSafari){ // safari
39162             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39163             v =  (v < 10) ? 10 : v;
39164             v =  (v > 48) ? 48 : v;
39165             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39166             
39167         }
39168         
39169         
39170         v = Math.max(1, v+adjust);
39171         
39172         this.execCmd('FontSize', v  );
39173     },
39174
39175     onEditorEvent : function(e){
39176         this.fireEvent('editorevent', this, e);
39177       //  this.updateToolbar();
39178         this.syncValue();
39179     },
39180
39181     insertTag : function(tg)
39182     {
39183         // could be a bit smarter... -> wrap the current selected tRoo..
39184         
39185         this.execCmd("formatblock",   tg);
39186         
39187     },
39188     
39189     insertText : function(txt)
39190     {
39191         
39192         
39193         range = this.createRange();
39194         range.deleteContents();
39195                //alert(Sender.getAttribute('label'));
39196                
39197         range.insertNode(this.doc.createTextNode(txt));
39198     } ,
39199     
39200     // private
39201     relayBtnCmd : function(btn){
39202         this.relayCmd(btn.cmd);
39203     },
39204
39205     /**
39206      * Executes a Midas editor command on the editor document and performs necessary focus and
39207      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39208      * @param {String} cmd The Midas command
39209      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39210      */
39211     relayCmd : function(cmd, value){
39212         this.win.focus();
39213         this.execCmd(cmd, value);
39214         this.fireEvent('editorevent', this);
39215         //this.updateToolbar();
39216         this.deferFocus();
39217     },
39218
39219     /**
39220      * Executes a Midas editor command directly on the editor document.
39221      * For visual commands, you should use {@link #relayCmd} instead.
39222      * <b>This should only be called after the editor is initialized.</b>
39223      * @param {String} cmd The Midas command
39224      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39225      */
39226     execCmd : function(cmd, value){
39227         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39228         this.syncValue();
39229     },
39230
39231    
39232     /**
39233      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39234      * to insert tRoo.
39235      * @param {String} text
39236      */
39237     insertAtCursor : function(text){
39238         if(!this.activated){
39239             return;
39240         }
39241         if(Roo.isIE){
39242             this.win.focus();
39243             var r = this.doc.selection.createRange();
39244             if(r){
39245                 r.collapse(true);
39246                 r.pasteHTML(text);
39247                 this.syncValue();
39248                 this.deferFocus();
39249             }
39250         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39251             this.win.focus();
39252             this.execCmd('InsertHTML', text);
39253             this.deferFocus();
39254         }
39255     },
39256  // private
39257     mozKeyPress : function(e){
39258         if(e.ctrlKey){
39259             var c = e.getCharCode(), cmd;
39260           
39261             if(c > 0){
39262                 c = String.fromCharCode(c).toLowerCase();
39263                 switch(c){
39264                     case 'b':
39265                         cmd = 'bold';
39266                     break;
39267                     case 'i':
39268                         cmd = 'italic';
39269                     break;
39270                     case 'u':
39271                         cmd = 'underline';
39272                     case 'v':
39273                         this.cleanUpPaste.defer(100, this);
39274                         return;
39275                     break;
39276                 }
39277                 if(cmd){
39278                     this.win.focus();
39279                     this.execCmd(cmd);
39280                     this.deferFocus();
39281                     e.preventDefault();
39282                 }
39283                 
39284             }
39285         }
39286     },
39287
39288     // private
39289     fixKeys : function(){ // load time branching for fastest keydown performance
39290         if(Roo.isIE){
39291             return function(e){
39292                 var k = e.getKey(), r;
39293                 if(k == e.TAB){
39294                     e.stopEvent();
39295                     r = this.doc.selection.createRange();
39296                     if(r){
39297                         r.collapse(true);
39298                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39299                         this.deferFocus();
39300                     }
39301                     return;
39302                 }
39303                 
39304                 if(k == e.ENTER){
39305                     r = this.doc.selection.createRange();
39306                     if(r){
39307                         var target = r.parentElement();
39308                         if(!target || target.tagName.toLowerCase() != 'li'){
39309                             e.stopEvent();
39310                             r.pasteHTML('<br />');
39311                             r.collapse(false);
39312                             r.select();
39313                         }
39314                     }
39315                 }
39316                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39317                     this.cleanUpPaste.defer(100, this);
39318                     return;
39319                 }
39320                 
39321                 
39322             };
39323         }else if(Roo.isOpera){
39324             return function(e){
39325                 var k = e.getKey();
39326                 if(k == e.TAB){
39327                     e.stopEvent();
39328                     this.win.focus();
39329                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39330                     this.deferFocus();
39331                 }
39332                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39333                     this.cleanUpPaste.defer(100, this);
39334                     return;
39335                 }
39336                 
39337             };
39338         }else if(Roo.isSafari){
39339             return function(e){
39340                 var k = e.getKey();
39341                 
39342                 if(k == e.TAB){
39343                     e.stopEvent();
39344                     this.execCmd('InsertText','\t');
39345                     this.deferFocus();
39346                     return;
39347                 }
39348                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39349                     this.cleanUpPaste.defer(100, this);
39350                     return;
39351                 }
39352                 
39353              };
39354         }
39355     }(),
39356     
39357     getAllAncestors: function()
39358     {
39359         var p = this.getSelectedNode();
39360         var a = [];
39361         if (!p) {
39362             a.push(p); // push blank onto stack..
39363             p = this.getParentElement();
39364         }
39365         
39366         
39367         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39368             a.push(p);
39369             p = p.parentNode;
39370         }
39371         a.push(this.doc.body);
39372         return a;
39373     },
39374     lastSel : false,
39375     lastSelNode : false,
39376     
39377     
39378     getSelection : function() 
39379     {
39380         this.assignDocWin();
39381         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39382     },
39383     
39384     getSelectedNode: function() 
39385     {
39386         // this may only work on Gecko!!!
39387         
39388         // should we cache this!!!!
39389         
39390         
39391         
39392          
39393         var range = this.createRange(this.getSelection()).cloneRange();
39394         
39395         if (Roo.isIE) {
39396             var parent = range.parentElement();
39397             while (true) {
39398                 var testRange = range.duplicate();
39399                 testRange.moveToElementText(parent);
39400                 if (testRange.inRange(range)) {
39401                     break;
39402                 }
39403                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39404                     break;
39405                 }
39406                 parent = parent.parentElement;
39407             }
39408             return parent;
39409         }
39410         
39411         // is ancestor a text element.
39412         var ac =  range.commonAncestorContainer;
39413         if (ac.nodeType == 3) {
39414             ac = ac.parentNode;
39415         }
39416         
39417         var ar = ac.childNodes;
39418          
39419         var nodes = [];
39420         var other_nodes = [];
39421         var has_other_nodes = false;
39422         for (var i=0;i<ar.length;i++) {
39423             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39424                 continue;
39425             }
39426             // fullly contained node.
39427             
39428             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39429                 nodes.push(ar[i]);
39430                 continue;
39431             }
39432             
39433             // probably selected..
39434             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39435                 other_nodes.push(ar[i]);
39436                 continue;
39437             }
39438             // outer..
39439             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39440                 continue;
39441             }
39442             
39443             
39444             has_other_nodes = true;
39445         }
39446         if (!nodes.length && other_nodes.length) {
39447             nodes= other_nodes;
39448         }
39449         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39450             return false;
39451         }
39452         
39453         return nodes[0];
39454     },
39455     createRange: function(sel)
39456     {
39457         // this has strange effects when using with 
39458         // top toolbar - not sure if it's a great idea.
39459         //this.editor.contentWindow.focus();
39460         if (typeof sel != "undefined") {
39461             try {
39462                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39463             } catch(e) {
39464                 return this.doc.createRange();
39465             }
39466         } else {
39467             return this.doc.createRange();
39468         }
39469     },
39470     getParentElement: function()
39471     {
39472         
39473         this.assignDocWin();
39474         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39475         
39476         var range = this.createRange(sel);
39477          
39478         try {
39479             var p = range.commonAncestorContainer;
39480             while (p.nodeType == 3) { // text node
39481                 p = p.parentNode;
39482             }
39483             return p;
39484         } catch (e) {
39485             return null;
39486         }
39487     
39488     },
39489     /***
39490      *
39491      * Range intersection.. the hard stuff...
39492      *  '-1' = before
39493      *  '0' = hits..
39494      *  '1' = after.
39495      *         [ -- selected range --- ]
39496      *   [fail]                        [fail]
39497      *
39498      *    basically..
39499      *      if end is before start or  hits it. fail.
39500      *      if start is after end or hits it fail.
39501      *
39502      *   if either hits (but other is outside. - then it's not 
39503      *   
39504      *    
39505      **/
39506     
39507     
39508     // @see http://www.thismuchiknow.co.uk/?p=64.
39509     rangeIntersectsNode : function(range, node)
39510     {
39511         var nodeRange = node.ownerDocument.createRange();
39512         try {
39513             nodeRange.selectNode(node);
39514         } catch (e) {
39515             nodeRange.selectNodeContents(node);
39516         }
39517     
39518         var rangeStartRange = range.cloneRange();
39519         rangeStartRange.collapse(true);
39520     
39521         var rangeEndRange = range.cloneRange();
39522         rangeEndRange.collapse(false);
39523     
39524         var nodeStartRange = nodeRange.cloneRange();
39525         nodeStartRange.collapse(true);
39526     
39527         var nodeEndRange = nodeRange.cloneRange();
39528         nodeEndRange.collapse(false);
39529     
39530         return rangeStartRange.compareBoundaryPoints(
39531                  Range.START_TO_START, nodeEndRange) == -1 &&
39532                rangeEndRange.compareBoundaryPoints(
39533                  Range.START_TO_START, nodeStartRange) == 1;
39534         
39535          
39536     },
39537     rangeCompareNode : function(range, node)
39538     {
39539         var nodeRange = node.ownerDocument.createRange();
39540         try {
39541             nodeRange.selectNode(node);
39542         } catch (e) {
39543             nodeRange.selectNodeContents(node);
39544         }
39545         
39546         
39547         range.collapse(true);
39548     
39549         nodeRange.collapse(true);
39550      
39551         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39552         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39553          
39554         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39555         
39556         var nodeIsBefore   =  ss == 1;
39557         var nodeIsAfter    = ee == -1;
39558         
39559         if (nodeIsBefore && nodeIsAfter)
39560             return 0; // outer
39561         if (!nodeIsBefore && nodeIsAfter)
39562             return 1; //right trailed.
39563         
39564         if (nodeIsBefore && !nodeIsAfter)
39565             return 2;  // left trailed.
39566         // fully contined.
39567         return 3;
39568     },
39569
39570     // private? - in a new class?
39571     cleanUpPaste :  function()
39572     {
39573         // cleans up the whole document..
39574       //  console.log('cleanuppaste');
39575         this.cleanUpChildren(this.doc.body);
39576         
39577         
39578     },
39579     cleanUpChildren : function (n)
39580     {
39581         if (!n.childNodes.length) {
39582             return;
39583         }
39584         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39585            this.cleanUpChild(n.childNodes[i]);
39586         }
39587     },
39588     
39589     
39590         
39591     
39592     cleanUpChild : function (node)
39593     {
39594         //console.log(node);
39595         if (node.nodeName == "#text") {
39596             // clean up silly Windows -- stuff?
39597             return; 
39598         }
39599         if (node.nodeName == "#comment") {
39600             node.parentNode.removeChild(node);
39601             // clean up silly Windows -- stuff?
39602             return; 
39603         }
39604         
39605         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39606             // remove node.
39607             node.parentNode.removeChild(node);
39608             return;
39609             
39610         }
39611         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39612             this.cleanUpChildren(node);
39613             // inserts everything just before this node...
39614             while (node.childNodes.length) {
39615                 var cn = node.childNodes[0];
39616                 node.removeChild(cn);
39617                 node.parentNode.insertBefore(cn, node);
39618             }
39619             node.parentNode.removeChild(node);
39620             return;
39621         }
39622         
39623         if (!node.attributes || !node.attributes.length) {
39624             this.cleanUpChildren(node);
39625             return;
39626         }
39627         
39628         function cleanAttr(n,v)
39629         {
39630             
39631             if (v.match(/^\./) || v.match(/^\//)) {
39632                 return;
39633             }
39634             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39635                 return;
39636             }
39637             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39638             node.removeAttribute(n);
39639             
39640         }
39641         
39642         function cleanStyle(n,v)
39643         {
39644             if (v.match(/expression/)) { //XSS?? should we even bother..
39645                 node.removeAttribute(n);
39646                 return;
39647             }
39648             
39649             
39650             var parts = v.split(/;/);
39651             Roo.each(parts, function(p) {
39652                 p = p.replace(/\s+/g,'');
39653                 if (!p.length) {
39654                     return true;
39655                 }
39656                 var l = p.split(':').shift().replace(/\s+/g,'');
39657                 
39658                 // only allow 'c whitelisted system attributes'
39659                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39660                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39661                     node.removeAttribute(n);
39662                     return false;
39663                 }
39664                 return true;
39665             });
39666             
39667             
39668         }
39669         
39670         
39671         for (var i = node.attributes.length-1; i > -1 ; i--) {
39672             var a = node.attributes[i];
39673             //console.log(a);
39674             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39675                 node.removeAttribute(a.name);
39676                 return;
39677             }
39678             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39679                 cleanAttr(a.name,a.value); // fixme..
39680                 return;
39681             }
39682             if (a.name == 'style') {
39683                 cleanStyle(a.name,a.value);
39684             }
39685             /// clean up MS crap..
39686             if (a.name == 'class') {
39687                 if (a.value.match(/^Mso/)) {
39688                     node.className = '';
39689                 }
39690             }
39691             
39692             // style cleanup!?
39693             // class cleanup?
39694             
39695         }
39696         
39697         
39698         this.cleanUpChildren(node);
39699         
39700         
39701     }
39702     
39703     
39704     // hide stuff that is not compatible
39705     /**
39706      * @event blur
39707      * @hide
39708      */
39709     /**
39710      * @event change
39711      * @hide
39712      */
39713     /**
39714      * @event focus
39715      * @hide
39716      */
39717     /**
39718      * @event specialkey
39719      * @hide
39720      */
39721     /**
39722      * @cfg {String} fieldClass @hide
39723      */
39724     /**
39725      * @cfg {String} focusClass @hide
39726      */
39727     /**
39728      * @cfg {String} autoCreate @hide
39729      */
39730     /**
39731      * @cfg {String} inputType @hide
39732      */
39733     /**
39734      * @cfg {String} invalidClass @hide
39735      */
39736     /**
39737      * @cfg {String} invalidText @hide
39738      */
39739     /**
39740      * @cfg {String} msgFx @hide
39741      */
39742     /**
39743      * @cfg {String} validateOnBlur @hide
39744      */
39745 });
39746
39747 Roo.form.HtmlEditor.white = [
39748         'area', 'br', 'img', 'input', 'hr', 'wbr',
39749         
39750        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39751        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39752        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39753        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39754        'table',   'ul',         'xmp', 
39755        
39756        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39757       'thead',   'tr', 
39758      
39759       'dir', 'menu', 'ol', 'ul', 'dl',
39760        
39761       'embed',  'object'
39762 ];
39763
39764
39765 Roo.form.HtmlEditor.black = [
39766     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39767         'applet', // 
39768         'base',   'basefont', 'bgsound', 'blink',  'body', 
39769         'frame',  'frameset', 'head',    'html',   'ilayer', 
39770         'iframe', 'layer',  'link',     'meta',    'object',   
39771         'script', 'style' ,'title',  'xml' // clean later..
39772 ];
39773 Roo.form.HtmlEditor.clean = [
39774     'script', 'style', 'title', 'xml'
39775 ];
39776 Roo.form.HtmlEditor.remove = [
39777     'font'
39778 ];
39779 // attributes..
39780
39781 Roo.form.HtmlEditor.ablack = [
39782     'on'
39783 ];
39784     
39785 Roo.form.HtmlEditor.aclean = [ 
39786     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39787 ];
39788
39789 // protocols..
39790 Roo.form.HtmlEditor.pwhite= [
39791         'http',  'https',  'mailto'
39792 ];
39793
39794 // white listed style attributes.
39795 Roo.form.HtmlEditor.cwhite= [
39796         'text-align',
39797         'font-size'
39798 ];
39799
39800 // <script type="text/javascript">
39801 /*
39802  * Based on
39803  * Ext JS Library 1.1.1
39804  * Copyright(c) 2006-2007, Ext JS, LLC.
39805  *  
39806  
39807  */
39808
39809 /**
39810  * @class Roo.form.HtmlEditorToolbar1
39811  * Basic Toolbar
39812  * 
39813  * Usage:
39814  *
39815  new Roo.form.HtmlEditor({
39816     ....
39817     toolbars : [
39818         new Roo.form.HtmlEditorToolbar1({
39819             disable : { fonts: 1 , format: 1, ..., ... , ...],
39820             btns : [ .... ]
39821         })
39822     }
39823      
39824  * 
39825  * @cfg {Object} disable List of elements to disable..
39826  * @cfg {Array} btns List of additional buttons.
39827  * 
39828  * 
39829  * NEEDS Extra CSS? 
39830  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39831  */
39832  
39833 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39834 {
39835     
39836     Roo.apply(this, config);
39837     
39838     // default disabled, based on 'good practice'..
39839     this.disable = this.disable || {};
39840     Roo.applyIf(this.disable, {
39841         fontSize : true,
39842         colors : true,
39843         specialElements : true
39844     });
39845     
39846     
39847     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39848     // dont call parent... till later.
39849 }
39850
39851 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39852     
39853     tb: false,
39854     
39855     rendered: false,
39856     
39857     editor : false,
39858     /**
39859      * @cfg {Object} disable  List of toolbar elements to disable
39860          
39861      */
39862     disable : false,
39863       /**
39864      * @cfg {Array} fontFamilies An array of available font families
39865      */
39866     fontFamilies : [
39867         'Arial',
39868         'Courier New',
39869         'Tahoma',
39870         'Times New Roman',
39871         'Verdana'
39872     ],
39873     
39874     specialChars : [
39875            "&#169;",
39876           "&#174;",     
39877           "&#8482;",    
39878           "&#163;" ,    
39879          // "&#8212;",    
39880           "&#8230;",    
39881           "&#247;" ,    
39882         //  "&#225;" ,     ?? a acute?
39883            "&#8364;"    , //Euro
39884        //   "&#8220;"    ,
39885         //  "&#8221;"    ,
39886         //  "&#8226;"    ,
39887           "&#176;"  //   , // degrees
39888
39889          // "&#233;"     , // e ecute
39890          // "&#250;"     , // u ecute?
39891     ],
39892     
39893     specialElements : [
39894         {
39895             text: "Insert Table",
39896             xtype: 'MenuItem',
39897             xns : Roo.Menu,
39898             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39899                 
39900         },
39901         {    
39902             text: "Insert Image",
39903             xtype: 'MenuItem',
39904             xns : Roo.Menu,
39905             ihtml : '<img src="about:blank"/>'
39906             
39907         }
39908         
39909          
39910     ],
39911     
39912     
39913     inputElements : [ 
39914             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39915             "input:submit", "input:button", "select", "textarea", "label" ],
39916     formats : [
39917         ["p"] ,  
39918         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39919         ["pre"],[ "code"], 
39920         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39921     ],
39922      /**
39923      * @cfg {String} defaultFont default font to use.
39924      */
39925     defaultFont: 'tahoma',
39926    
39927     fontSelect : false,
39928     
39929     
39930     formatCombo : false,
39931     
39932     init : function(editor)
39933     {
39934         this.editor = editor;
39935         
39936         
39937         var fid = editor.frameId;
39938         var etb = this;
39939         function btn(id, toggle, handler){
39940             var xid = fid + '-'+ id ;
39941             return {
39942                 id : xid,
39943                 cmd : id,
39944                 cls : 'x-btn-icon x-edit-'+id,
39945                 enableToggle:toggle !== false,
39946                 scope: editor, // was editor...
39947                 handler:handler||editor.relayBtnCmd,
39948                 clickEvent:'mousedown',
39949                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39950                 tabIndex:-1
39951             };
39952         }
39953         
39954         
39955         
39956         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39957         this.tb = tb;
39958          // stop form submits
39959         tb.el.on('click', function(e){
39960             e.preventDefault(); // what does this do?
39961         });
39962
39963         if(!this.disable.font && !Roo.isSafari){
39964             /* why no safari for fonts
39965             editor.fontSelect = tb.el.createChild({
39966                 tag:'select',
39967                 tabIndex: -1,
39968                 cls:'x-font-select',
39969                 html: editor.createFontOptions()
39970             });
39971             editor.fontSelect.on('change', function(){
39972                 var font = editor.fontSelect.dom.value;
39973                 editor.relayCmd('fontname', font);
39974                 editor.deferFocus();
39975             }, editor);
39976             tb.add(
39977                 editor.fontSelect.dom,
39978                 '-'
39979             );
39980             */
39981         };
39982         if(!this.disable.formats){
39983             this.formatCombo = new Roo.form.ComboBox({
39984                 store: new Roo.data.SimpleStore({
39985                     id : 'tag',
39986                     fields: ['tag'],
39987                     data : this.formats // from states.js
39988                 }),
39989                 blockFocus : true,
39990                 //autoCreate : {tag: "div",  size: "20"},
39991                 displayField:'tag',
39992                 typeAhead: false,
39993                 mode: 'local',
39994                 editable : false,
39995                 triggerAction: 'all',
39996                 emptyText:'Add tag',
39997                 selectOnFocus:true,
39998                 width:135,
39999                 listeners : {
40000                     'select': function(c, r, i) {
40001                         editor.insertTag(r.get('tag'));
40002                         editor.focus();
40003                     }
40004                 }
40005
40006             });
40007             tb.addField(this.formatCombo);
40008             
40009         }
40010         
40011         if(!this.disable.format){
40012             tb.add(
40013                 btn('bold'),
40014                 btn('italic'),
40015                 btn('underline')
40016             );
40017         };
40018         if(!this.disable.fontSize){
40019             tb.add(
40020                 '-',
40021                 
40022                 
40023                 btn('increasefontsize', false, editor.adjustFont),
40024                 btn('decreasefontsize', false, editor.adjustFont)
40025             );
40026         };
40027         
40028         
40029         if(!this.disable.colors){
40030             tb.add(
40031                 '-', {
40032                     id:editor.frameId +'-forecolor',
40033                     cls:'x-btn-icon x-edit-forecolor',
40034                     clickEvent:'mousedown',
40035                     tooltip: this.buttonTips['forecolor'] || undefined,
40036                     tabIndex:-1,
40037                     menu : new Roo.menu.ColorMenu({
40038                         allowReselect: true,
40039                         focus: Roo.emptyFn,
40040                         value:'000000',
40041                         plain:true,
40042                         selectHandler: function(cp, color){
40043                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40044                             editor.deferFocus();
40045                         },
40046                         scope: editor,
40047                         clickEvent:'mousedown'
40048                     })
40049                 }, {
40050                     id:editor.frameId +'backcolor',
40051                     cls:'x-btn-icon x-edit-backcolor',
40052                     clickEvent:'mousedown',
40053                     tooltip: this.buttonTips['backcolor'] || undefined,
40054                     tabIndex:-1,
40055                     menu : new Roo.menu.ColorMenu({
40056                         focus: Roo.emptyFn,
40057                         value:'FFFFFF',
40058                         plain:true,
40059                         allowReselect: true,
40060                         selectHandler: function(cp, color){
40061                             if(Roo.isGecko){
40062                                 editor.execCmd('useCSS', false);
40063                                 editor.execCmd('hilitecolor', color);
40064                                 editor.execCmd('useCSS', true);
40065                                 editor.deferFocus();
40066                             }else{
40067                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40068                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40069                                 editor.deferFocus();
40070                             }
40071                         },
40072                         scope:editor,
40073                         clickEvent:'mousedown'
40074                     })
40075                 }
40076             );
40077         };
40078         // now add all the items...
40079         
40080
40081         if(!this.disable.alignments){
40082             tb.add(
40083                 '-',
40084                 btn('justifyleft'),
40085                 btn('justifycenter'),
40086                 btn('justifyright')
40087             );
40088         };
40089
40090         //if(!Roo.isSafari){
40091             if(!this.disable.links){
40092                 tb.add(
40093                     '-',
40094                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40095                 );
40096             };
40097
40098             if(!this.disable.lists){
40099                 tb.add(
40100                     '-',
40101                     btn('insertorderedlist'),
40102                     btn('insertunorderedlist')
40103                 );
40104             }
40105             if(!this.disable.sourceEdit){
40106                 tb.add(
40107                     '-',
40108                     btn('sourceedit', true, function(btn){
40109                         this.toggleSourceEdit(btn.pressed);
40110                     })
40111                 );
40112             }
40113         //}
40114         
40115         var smenu = { };
40116         // special menu.. - needs to be tidied up..
40117         if (!this.disable.special) {
40118             smenu = {
40119                 text: "&#169;",
40120                 cls: 'x-edit-none',
40121                 
40122                 menu : {
40123                     items : []
40124                 }
40125             };
40126             for (var i =0; i < this.specialChars.length; i++) {
40127                 smenu.menu.items.push({
40128                     
40129                     html: this.specialChars[i],
40130                     handler: function(a,b) {
40131                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40132                         
40133                     },
40134                     tabIndex:-1
40135                 });
40136             }
40137             
40138             
40139             tb.add(smenu);
40140             
40141             
40142         }
40143          
40144         if (!this.disable.specialElements) {
40145             var semenu = {
40146                 text: "Other;",
40147                 cls: 'x-edit-none',
40148                 menu : {
40149                     items : []
40150                 }
40151             };
40152             for (var i =0; i < this.specialElements.length; i++) {
40153                 semenu.menu.items.push(
40154                     Roo.apply({ 
40155                         handler: function(a,b) {
40156                             editor.insertAtCursor(this.ihtml);
40157                         }
40158                     }, this.specialElements[i])
40159                 );
40160                     
40161             }
40162             
40163             tb.add(semenu);
40164             
40165             
40166         }
40167          
40168         
40169         if (this.btns) {
40170             for(var i =0; i< this.btns.length;i++) {
40171                 var b = this.btns[i];
40172                 b.cls =  'x-edit-none';
40173                 b.scope = editor;
40174                 tb.add(b);
40175             }
40176         
40177         }
40178         
40179         
40180         
40181         // disable everything...
40182         
40183         this.tb.items.each(function(item){
40184            if(item.id != editor.frameId+ '-sourceedit'){
40185                 item.disable();
40186             }
40187         });
40188         this.rendered = true;
40189         
40190         // the all the btns;
40191         editor.on('editorevent', this.updateToolbar, this);
40192         // other toolbars need to implement this..
40193         //editor.on('editmodechange', this.updateToolbar, this);
40194     },
40195     
40196     
40197     
40198     /**
40199      * Protected method that will not generally be called directly. It triggers
40200      * a toolbar update by reading the markup state of the current selection in the editor.
40201      */
40202     updateToolbar: function(){
40203
40204         if(!this.editor.activated){
40205             this.editor.onFirstFocus();
40206             return;
40207         }
40208
40209         var btns = this.tb.items.map, 
40210             doc = this.editor.doc,
40211             frameId = this.editor.frameId;
40212
40213         if(!this.disable.font && !Roo.isSafari){
40214             /*
40215             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40216             if(name != this.fontSelect.dom.value){
40217                 this.fontSelect.dom.value = name;
40218             }
40219             */
40220         }
40221         if(!this.disable.format){
40222             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40223             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40224             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40225         }
40226         if(!this.disable.alignments){
40227             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40228             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40229             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40230         }
40231         if(!Roo.isSafari && !this.disable.lists){
40232             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40233             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40234         }
40235         
40236         var ans = this.editor.getAllAncestors();
40237         if (this.formatCombo) {
40238             
40239             
40240             var store = this.formatCombo.store;
40241             this.formatCombo.setValue("");
40242             for (var i =0; i < ans.length;i++) {
40243                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40244                     // select it..
40245                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40246                     break;
40247                 }
40248             }
40249         }
40250         
40251         
40252         
40253         // hides menus... - so this cant be on a menu...
40254         Roo.menu.MenuMgr.hideAll();
40255
40256         //this.editorsyncValue();
40257     },
40258    
40259     
40260     createFontOptions : function(){
40261         var buf = [], fs = this.fontFamilies, ff, lc;
40262         for(var i = 0, len = fs.length; i< len; i++){
40263             ff = fs[i];
40264             lc = ff.toLowerCase();
40265             buf.push(
40266                 '<option value="',lc,'" style="font-family:',ff,';"',
40267                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40268                     ff,
40269                 '</option>'
40270             );
40271         }
40272         return buf.join('');
40273     },
40274     
40275     toggleSourceEdit : function(sourceEditMode){
40276         if(sourceEditMode === undefined){
40277             sourceEditMode = !this.sourceEditMode;
40278         }
40279         this.sourceEditMode = sourceEditMode === true;
40280         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40281         // just toggle the button?
40282         if(btn.pressed !== this.editor.sourceEditMode){
40283             btn.toggle(this.editor.sourceEditMode);
40284             return;
40285         }
40286         
40287         if(this.sourceEditMode){
40288             this.tb.items.each(function(item){
40289                 if(item.cmd != 'sourceedit'){
40290                     item.disable();
40291                 }
40292             });
40293           
40294         }else{
40295             if(this.initialized){
40296                 this.tb.items.each(function(item){
40297                     item.enable();
40298                 });
40299             }
40300             
40301         }
40302         // tell the editor that it's been pressed..
40303         this.editor.toggleSourceEdit(sourceEditMode);
40304        
40305     },
40306      /**
40307      * Object collection of toolbar tooltips for the buttons in the editor. The key
40308      * is the command id associated with that button and the value is a valid QuickTips object.
40309      * For example:
40310 <pre><code>
40311 {
40312     bold : {
40313         title: 'Bold (Ctrl+B)',
40314         text: 'Make the selected text bold.',
40315         cls: 'x-html-editor-tip'
40316     },
40317     italic : {
40318         title: 'Italic (Ctrl+I)',
40319         text: 'Make the selected text italic.',
40320         cls: 'x-html-editor-tip'
40321     },
40322     ...
40323 </code></pre>
40324     * @type Object
40325      */
40326     buttonTips : {
40327         bold : {
40328             title: 'Bold (Ctrl+B)',
40329             text: 'Make the selected text bold.',
40330             cls: 'x-html-editor-tip'
40331         },
40332         italic : {
40333             title: 'Italic (Ctrl+I)',
40334             text: 'Make the selected text italic.',
40335             cls: 'x-html-editor-tip'
40336         },
40337         underline : {
40338             title: 'Underline (Ctrl+U)',
40339             text: 'Underline the selected text.',
40340             cls: 'x-html-editor-tip'
40341         },
40342         increasefontsize : {
40343             title: 'Grow Text',
40344             text: 'Increase the font size.',
40345             cls: 'x-html-editor-tip'
40346         },
40347         decreasefontsize : {
40348             title: 'Shrink Text',
40349             text: 'Decrease the font size.',
40350             cls: 'x-html-editor-tip'
40351         },
40352         backcolor : {
40353             title: 'Text Highlight Color',
40354             text: 'Change the background color of the selected text.',
40355             cls: 'x-html-editor-tip'
40356         },
40357         forecolor : {
40358             title: 'Font Color',
40359             text: 'Change the color of the selected text.',
40360             cls: 'x-html-editor-tip'
40361         },
40362         justifyleft : {
40363             title: 'Align Text Left',
40364             text: 'Align text to the left.',
40365             cls: 'x-html-editor-tip'
40366         },
40367         justifycenter : {
40368             title: 'Center Text',
40369             text: 'Center text in the editor.',
40370             cls: 'x-html-editor-tip'
40371         },
40372         justifyright : {
40373             title: 'Align Text Right',
40374             text: 'Align text to the right.',
40375             cls: 'x-html-editor-tip'
40376         },
40377         insertunorderedlist : {
40378             title: 'Bullet List',
40379             text: 'Start a bulleted list.',
40380             cls: 'x-html-editor-tip'
40381         },
40382         insertorderedlist : {
40383             title: 'Numbered List',
40384             text: 'Start a numbered list.',
40385             cls: 'x-html-editor-tip'
40386         },
40387         createlink : {
40388             title: 'Hyperlink',
40389             text: 'Make the selected text a hyperlink.',
40390             cls: 'x-html-editor-tip'
40391         },
40392         sourceedit : {
40393             title: 'Source Edit',
40394             text: 'Switch to source editing mode.',
40395             cls: 'x-html-editor-tip'
40396         }
40397     },
40398     // private
40399     onDestroy : function(){
40400         if(this.rendered){
40401             
40402             this.tb.items.each(function(item){
40403                 if(item.menu){
40404                     item.menu.removeAll();
40405                     if(item.menu.el){
40406                         item.menu.el.destroy();
40407                     }
40408                 }
40409                 item.destroy();
40410             });
40411              
40412         }
40413     },
40414     onFirstFocus: function() {
40415         this.tb.items.each(function(item){
40416            item.enable();
40417         });
40418     }
40419 });
40420
40421
40422
40423
40424 // <script type="text/javascript">
40425 /*
40426  * Based on
40427  * Ext JS Library 1.1.1
40428  * Copyright(c) 2006-2007, Ext JS, LLC.
40429  *  
40430  
40431  */
40432
40433  
40434 /**
40435  * @class Roo.form.HtmlEditor.ToolbarContext
40436  * Context Toolbar
40437  * 
40438  * Usage:
40439  *
40440  new Roo.form.HtmlEditor({
40441     ....
40442     toolbars : [
40443         { xtype: 'ToolbarStandard', styles : {} }
40444         { xtype: 'ToolbarContext', disable : {} }
40445     ]
40446 })
40447
40448      
40449  * 
40450  * @config : {Object} disable List of elements to disable.. (not done yet.)
40451  * @config : {Object} styles  Map of styles available.
40452  * 
40453  */
40454
40455 Roo.form.HtmlEditor.ToolbarContext = function(config)
40456 {
40457     
40458     Roo.apply(this, config);
40459     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40460     // dont call parent... till later.
40461     this.styles = this.styles || {};
40462 }
40463 Roo.form.HtmlEditor.ToolbarContext.types = {
40464     'IMG' : {
40465         width : {
40466             title: "Width",
40467             width: 40
40468         },
40469         height:  {
40470             title: "Height",
40471             width: 40
40472         },
40473         align: {
40474             title: "Align",
40475             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40476             width : 80
40477             
40478         },
40479         border: {
40480             title: "Border",
40481             width: 40
40482         },
40483         alt: {
40484             title: "Alt",
40485             width: 120
40486         },
40487         src : {
40488             title: "Src",
40489             width: 220
40490         }
40491         
40492     },
40493     'A' : {
40494         name : {
40495             title: "Name",
40496             width: 50
40497         },
40498         href:  {
40499             title: "Href",
40500             width: 220
40501         } // border?
40502         
40503     },
40504     'TABLE' : {
40505         rows : {
40506             title: "Rows",
40507             width: 20
40508         },
40509         cols : {
40510             title: "Cols",
40511             width: 20
40512         },
40513         width : {
40514             title: "Width",
40515             width: 40
40516         },
40517         height : {
40518             title: "Height",
40519             width: 40
40520         },
40521         border : {
40522             title: "Border",
40523             width: 20
40524         }
40525     },
40526     'TD' : {
40527         width : {
40528             title: "Width",
40529             width: 40
40530         },
40531         height : {
40532             title: "Height",
40533             width: 40
40534         },   
40535         align: {
40536             title: "Align",
40537             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40538             width: 80
40539         },
40540         valign: {
40541             title: "Valign",
40542             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40543             width: 80
40544         },
40545         colspan: {
40546             title: "Colspan",
40547             width: 20
40548             
40549         }
40550     },
40551     'INPUT' : {
40552         name : {
40553             title: "name",
40554             width: 120
40555         },
40556         value : {
40557             title: "Value",
40558             width: 120
40559         },
40560         width : {
40561             title: "Width",
40562             width: 40
40563         }
40564     },
40565     'LABEL' : {
40566         'for' : {
40567             title: "For",
40568             width: 120
40569         }
40570     },
40571     'TEXTAREA' : {
40572           name : {
40573             title: "name",
40574             width: 120
40575         },
40576         rows : {
40577             title: "Rows",
40578             width: 20
40579         },
40580         cols : {
40581             title: "Cols",
40582             width: 20
40583         }
40584     },
40585     'SELECT' : {
40586         name : {
40587             title: "name",
40588             width: 120
40589         },
40590         selectoptions : {
40591             title: "Options",
40592             width: 200
40593         }
40594     },
40595     
40596     // should we really allow this??
40597     // should this just be 
40598     'BODY' : {
40599         title : {
40600             title: "title",
40601             width: 200,
40602             disabled : true
40603         }
40604     },
40605     '*' : {
40606         // empty..
40607     }
40608 };
40609
40610
40611
40612 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40613     
40614     tb: false,
40615     
40616     rendered: false,
40617     
40618     editor : false,
40619     /**
40620      * @cfg {Object} disable  List of toolbar elements to disable
40621          
40622      */
40623     disable : false,
40624     /**
40625      * @cfg {Object} styles List of styles 
40626      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40627      *
40628      * These must be defined in the page, so they get rendered correctly..
40629      * .headline { }
40630      * TD.underline { }
40631      * 
40632      */
40633     styles : false,
40634     
40635     
40636     
40637     toolbars : false,
40638     
40639     init : function(editor)
40640     {
40641         this.editor = editor;
40642         
40643         
40644         var fid = editor.frameId;
40645         var etb = this;
40646         function btn(id, toggle, handler){
40647             var xid = fid + '-'+ id ;
40648             return {
40649                 id : xid,
40650                 cmd : id,
40651                 cls : 'x-btn-icon x-edit-'+id,
40652                 enableToggle:toggle !== false,
40653                 scope: editor, // was editor...
40654                 handler:handler||editor.relayBtnCmd,
40655                 clickEvent:'mousedown',
40656                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40657                 tabIndex:-1
40658             };
40659         }
40660         // create a new element.
40661         var wdiv = editor.wrap.createChild({
40662                 tag: 'div'
40663             }, editor.wrap.dom.firstChild.nextSibling, true);
40664         
40665         // can we do this more than once??
40666         
40667          // stop form submits
40668       
40669  
40670         // disable everything...
40671         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40672         this.toolbars = {};
40673            
40674         for (var i in  ty) {
40675           
40676             this.toolbars[i] = this.buildToolbar(ty[i],i);
40677         }
40678         this.tb = this.toolbars.BODY;
40679         this.tb.el.show();
40680         this.buildFooter();
40681         this.footer.show();
40682          
40683         this.rendered = true;
40684         
40685         // the all the btns;
40686         editor.on('editorevent', this.updateToolbar, this);
40687         // other toolbars need to implement this..
40688         //editor.on('editmodechange', this.updateToolbar, this);
40689     },
40690     
40691     
40692     
40693     /**
40694      * Protected method that will not generally be called directly. It triggers
40695      * a toolbar update by reading the markup state of the current selection in the editor.
40696      */
40697     updateToolbar: function(ignore_a,ignore_b,sel){
40698
40699         
40700         if(!this.editor.activated){
40701              this.editor.onFirstFocus();
40702             return;
40703         }
40704         var updateFooter = sel ? false : true;
40705         
40706         
40707         var ans = this.editor.getAllAncestors();
40708         
40709         // pick
40710         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40711         
40712         if (!sel) { 
40713             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40714             sel = sel ? sel : this.editor.doc.body;
40715             sel = sel.tagName.length ? sel : this.editor.doc.body;
40716             
40717         }
40718         // pick a menu that exists..
40719         var tn = sel.tagName.toUpperCase();
40720         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40721         
40722         tn = sel.tagName.toUpperCase();
40723         
40724         var lastSel = this.tb.selectedNode
40725         
40726         this.tb.selectedNode = sel;
40727         
40728         // if current menu does not match..
40729         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40730                 
40731             this.tb.el.hide();
40732             ///console.log("show: " + tn);
40733             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40734             this.tb.el.show();
40735             // update name
40736             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40737             
40738             
40739             // update attributes
40740             if (this.tb.fields) {
40741                 this.tb.fields.each(function(e) {
40742                    e.setValue(sel.getAttribute(e.name));
40743                 });
40744             }
40745             
40746             // update styles
40747             var st = this.tb.fields.item(0);
40748             st.store.removeAll();
40749             var cn = sel.className.split(/\s+/);
40750             
40751             var avs = [];
40752             if (this.styles['*']) {
40753                 
40754                 Roo.each(this.styles['*'], function(v) {
40755                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40756                 });
40757             }
40758             if (this.styles[tn]) { 
40759                 Roo.each(this.styles[tn], function(v) {
40760                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40761                 });
40762             }
40763             
40764             st.store.loadData(avs);
40765             st.collapse();
40766             st.setValue(cn);
40767             
40768             // flag our selected Node.
40769             this.tb.selectedNode = sel;
40770            
40771            
40772             Roo.menu.MenuMgr.hideAll();
40773
40774         }
40775         
40776         if (!updateFooter) {
40777             return;
40778         }
40779         // update the footer
40780         //
40781         var html = '';
40782         
40783         this.footerEls = ans.reverse();
40784         Roo.each(this.footerEls, function(a,i) {
40785             if (!a) { return; }
40786             html += html.length ? ' &gt; '  :  '';
40787             
40788             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40789             
40790         });
40791        
40792         // 
40793         var sz = this.footDisp.up('td').getSize();
40794         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40795         this.footDisp.dom.style.marginLeft = '5px';
40796         
40797         this.footDisp.dom.style.overflow = 'hidden';
40798         
40799         this.footDisp.dom.innerHTML = html;
40800             
40801         //this.editorsyncValue();
40802     },
40803    
40804        
40805     // private
40806     onDestroy : function(){
40807         if(this.rendered){
40808             
40809             this.tb.items.each(function(item){
40810                 if(item.menu){
40811                     item.menu.removeAll();
40812                     if(item.menu.el){
40813                         item.menu.el.destroy();
40814                     }
40815                 }
40816                 item.destroy();
40817             });
40818              
40819         }
40820     },
40821     onFirstFocus: function() {
40822         // need to do this for all the toolbars..
40823         this.tb.items.each(function(item){
40824            item.enable();
40825         });
40826     },
40827     buildToolbar: function(tlist, nm)
40828     {
40829         var editor = this.editor;
40830          // create a new element.
40831         var wdiv = editor.wrap.createChild({
40832                 tag: 'div'
40833             }, editor.wrap.dom.firstChild.nextSibling, true);
40834         
40835        
40836         var tb = new Roo.Toolbar(wdiv);
40837         // add the name..
40838         
40839         tb.add(nm+ ":&nbsp;");
40840         
40841         // styles...
40842         if (this.styles) {
40843             
40844             // this needs a multi-select checkbox...
40845             tb.addField( new Roo.form.ComboBox({
40846                 store: new Roo.data.SimpleStore({
40847                     id : 'val',
40848                     fields: ['val', 'selected'],
40849                     data : [] 
40850                 }),
40851                 name : 'className',
40852                 displayField:'val',
40853                 typeAhead: false,
40854                 mode: 'local',
40855                 editable : false,
40856                 triggerAction: 'all',
40857                 emptyText:'Select Style',
40858                 selectOnFocus:true,
40859                 width: 130,
40860                 listeners : {
40861                     'select': function(c, r, i) {
40862                         // initial support only for on class per el..
40863                         tb.selectedNode.className =  r ? r.get('val') : '';
40864                     }
40865                 }
40866     
40867             }));
40868         }
40869             
40870         
40871         
40872         for (var i in tlist) {
40873             
40874             var item = tlist[i];
40875             tb.add(item.title + ":&nbsp;");
40876             
40877             
40878             
40879             
40880             if (item.opts) {
40881                 // opts == pulldown..
40882                 tb.addField( new Roo.form.ComboBox({
40883                     store: new Roo.data.SimpleStore({
40884                         id : 'val',
40885                         fields: ['val'],
40886                         data : item.opts  
40887                     }),
40888                     name : i,
40889                     displayField:'val',
40890                     typeAhead: false,
40891                     mode: 'local',
40892                     editable : false,
40893                     triggerAction: 'all',
40894                     emptyText:'Select',
40895                     selectOnFocus:true,
40896                     width: item.width ? item.width  : 130,
40897                     listeners : {
40898                         'select': function(c, r, i) {
40899                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40900                         }
40901                     }
40902
40903                 }));
40904                 continue;
40905                     
40906                  
40907                 
40908                 tb.addField( new Roo.form.TextField({
40909                     name: i,
40910                     width: 100,
40911                     //allowBlank:false,
40912                     value: ''
40913                 }));
40914                 continue;
40915             }
40916             tb.addField( new Roo.form.TextField({
40917                 name: i,
40918                 width: item.width,
40919                 //allowBlank:true,
40920                 value: '',
40921                 listeners: {
40922                     'change' : function(f, nv, ov) {
40923                         tb.selectedNode.setAttribute(f.name, nv);
40924                     }
40925                 }
40926             }));
40927              
40928         }
40929         tb.el.on('click', function(e){
40930             e.preventDefault(); // what does this do?
40931         });
40932         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40933         tb.el.hide();
40934         tb.name = nm;
40935         // dont need to disable them... as they will get hidden
40936         return tb;
40937          
40938         
40939     },
40940     buildFooter : function()
40941     {
40942         
40943         var fel = this.editor.wrap.createChild();
40944         this.footer = new Roo.Toolbar(fel);
40945         // toolbar has scrolly on left / right?
40946         var footDisp= new Roo.Toolbar.Fill();
40947         var _t = this;
40948         this.footer.add(
40949             {
40950                 text : '&lt;',
40951                 xtype: 'Button',
40952                 handler : function() {
40953                     _t.footDisp.scrollTo('left',0,true)
40954                 }
40955             }
40956         );
40957         this.footer.add( footDisp );
40958         this.footer.add( 
40959             {
40960                 text : '&gt;',
40961                 xtype: 'Button',
40962                 handler : function() {
40963                     // no animation..
40964                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40965                 }
40966             }
40967         );
40968         var fel = Roo.get(footDisp.el);
40969         fel.addClass('x-editor-context');
40970         this.footDispWrap = fel; 
40971         this.footDispWrap.overflow  = 'hidden';
40972         
40973         this.footDisp = fel.createChild();
40974         this.footDispWrap.on('click', this.onContextClick, this)
40975         
40976         
40977     },
40978     onContextClick : function (ev,dom)
40979     {
40980         ev.preventDefault();
40981         var  cn = dom.className;
40982         Roo.log(cn);
40983         if (!cn.match(/x-ed-loc-/)) {
40984             return;
40985         }
40986         var n = cn.split('-').pop();
40987         var ans = this.footerEls;
40988         var sel = ans[n];
40989         
40990          // pick
40991         var range = this.editor.createRange();
40992         
40993         range.selectNodeContents(sel);
40994         //range.selectNode(sel);
40995         
40996         
40997         var selection = this.editor.getSelection();
40998         selection.removeAllRanges();
40999         selection.addRange(range);
41000         
41001         
41002         
41003         this.updateToolbar(null, null, sel);
41004         
41005         
41006     }
41007     
41008     
41009     
41010     
41011     
41012 });
41013
41014
41015
41016
41017
41018 /*
41019  * Based on:
41020  * Ext JS Library 1.1.1
41021  * Copyright(c) 2006-2007, Ext JS, LLC.
41022  *
41023  * Originally Released Under LGPL - original licence link has changed is not relivant.
41024  *
41025  * Fork - LGPL
41026  * <script type="text/javascript">
41027  */
41028  
41029 /**
41030  * @class Roo.form.BasicForm
41031  * @extends Roo.util.Observable
41032  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41033  * @constructor
41034  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41035  * @param {Object} config Configuration options
41036  */
41037 Roo.form.BasicForm = function(el, config){
41038     this.allItems = [];
41039     this.childForms = [];
41040     Roo.apply(this, config);
41041     /*
41042      * The Roo.form.Field items in this form.
41043      * @type MixedCollection
41044      */
41045      
41046      
41047     this.items = new Roo.util.MixedCollection(false, function(o){
41048         return o.id || (o.id = Roo.id());
41049     });
41050     this.addEvents({
41051         /**
41052          * @event beforeaction
41053          * Fires before any action is performed. Return false to cancel the action.
41054          * @param {Form} this
41055          * @param {Action} action The action to be performed
41056          */
41057         beforeaction: true,
41058         /**
41059          * @event actionfailed
41060          * Fires when an action fails.
41061          * @param {Form} this
41062          * @param {Action} action The action that failed
41063          */
41064         actionfailed : true,
41065         /**
41066          * @event actioncomplete
41067          * Fires when an action is completed.
41068          * @param {Form} this
41069          * @param {Action} action The action that completed
41070          */
41071         actioncomplete : true
41072     });
41073     if(el){
41074         this.initEl(el);
41075     }
41076     Roo.form.BasicForm.superclass.constructor.call(this);
41077 };
41078
41079 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41080     /**
41081      * @cfg {String} method
41082      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41083      */
41084     /**
41085      * @cfg {DataReader} reader
41086      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41087      * This is optional as there is built-in support for processing JSON.
41088      */
41089     /**
41090      * @cfg {DataReader} errorReader
41091      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41092      * This is completely optional as there is built-in support for processing JSON.
41093      */
41094     /**
41095      * @cfg {String} url
41096      * The URL to use for form actions if one isn't supplied in the action options.
41097      */
41098     /**
41099      * @cfg {Boolean} fileUpload
41100      * Set to true if this form is a file upload.
41101      */
41102      
41103     /**
41104      * @cfg {Object} baseParams
41105      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41106      */
41107      /**
41108      
41109     /**
41110      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41111      */
41112     timeout: 30,
41113
41114     // private
41115     activeAction : null,
41116
41117     /**
41118      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41119      * or setValues() data instead of when the form was first created.
41120      */
41121     trackResetOnLoad : false,
41122     
41123     
41124     /**
41125      * childForms - used for multi-tab forms
41126      * @type {Array}
41127      */
41128     childForms : false,
41129     
41130     /**
41131      * allItems - full list of fields.
41132      * @type {Array}
41133      */
41134     allItems : false,
41135     
41136     /**
41137      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41138      * element by passing it or its id or mask the form itself by passing in true.
41139      * @type Mixed
41140      */
41141     waitMsgTarget : false,
41142
41143     // private
41144     initEl : function(el){
41145         this.el = Roo.get(el);
41146         this.id = this.el.id || Roo.id();
41147         this.el.on('submit', this.onSubmit, this);
41148         this.el.addClass('x-form');
41149     },
41150
41151     // private
41152     onSubmit : function(e){
41153         e.stopEvent();
41154     },
41155
41156     /**
41157      * Returns true if client-side validation on the form is successful.
41158      * @return Boolean
41159      */
41160     isValid : function(){
41161         var valid = true;
41162         this.items.each(function(f){
41163            if(!f.validate()){
41164                valid = false;
41165            }
41166         });
41167         return valid;
41168     },
41169
41170     /**
41171      * Returns true if any fields in this form have changed since their original load.
41172      * @return Boolean
41173      */
41174     isDirty : function(){
41175         var dirty = false;
41176         this.items.each(function(f){
41177            if(f.isDirty()){
41178                dirty = true;
41179                return false;
41180            }
41181         });
41182         return dirty;
41183     },
41184
41185     /**
41186      * Performs a predefined action (submit or load) or custom actions you define on this form.
41187      * @param {String} actionName The name of the action type
41188      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41189      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41190      * accept other config options):
41191      * <pre>
41192 Property          Type             Description
41193 ----------------  ---------------  ----------------------------------------------------------------------------------
41194 url               String           The url for the action (defaults to the form's url)
41195 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41196 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41197 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41198                                    validate the form on the client (defaults to false)
41199      * </pre>
41200      * @return {BasicForm} this
41201      */
41202     doAction : function(action, options){
41203         if(typeof action == 'string'){
41204             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41205         }
41206         if(this.fireEvent('beforeaction', this, action) !== false){
41207             this.beforeAction(action);
41208             action.run.defer(100, action);
41209         }
41210         return this;
41211     },
41212
41213     /**
41214      * Shortcut to do a submit action.
41215      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41216      * @return {BasicForm} this
41217      */
41218     submit : function(options){
41219         this.doAction('submit', options);
41220         return this;
41221     },
41222
41223     /**
41224      * Shortcut to do a load action.
41225      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41226      * @return {BasicForm} this
41227      */
41228     load : function(options){
41229         this.doAction('load', options);
41230         return this;
41231     },
41232
41233     /**
41234      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41235      * @param {Record} record The record to edit
41236      * @return {BasicForm} this
41237      */
41238     updateRecord : function(record){
41239         record.beginEdit();
41240         var fs = record.fields;
41241         fs.each(function(f){
41242             var field = this.findField(f.name);
41243             if(field){
41244                 record.set(f.name, field.getValue());
41245             }
41246         }, this);
41247         record.endEdit();
41248         return this;
41249     },
41250
41251     /**
41252      * Loads an Roo.data.Record into this form.
41253      * @param {Record} record The record to load
41254      * @return {BasicForm} this
41255      */
41256     loadRecord : function(record){
41257         this.setValues(record.data);
41258         return this;
41259     },
41260
41261     // private
41262     beforeAction : function(action){
41263         var o = action.options;
41264         
41265        
41266         if(this.waitMsgTarget === true){
41267             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41268         }else if(this.waitMsgTarget){
41269             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41270             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41271         }else {
41272             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41273         }
41274          
41275     },
41276
41277     // private
41278     afterAction : function(action, success){
41279         this.activeAction = null;
41280         var o = action.options;
41281         
41282         if(this.waitMsgTarget === true){
41283             this.el.unmask();
41284         }else if(this.waitMsgTarget){
41285             this.waitMsgTarget.unmask();
41286         }else{
41287             Roo.MessageBox.updateProgress(1);
41288             Roo.MessageBox.hide();
41289         }
41290          
41291         if(success){
41292             if(o.reset){
41293                 this.reset();
41294             }
41295             Roo.callback(o.success, o.scope, [this, action]);
41296             this.fireEvent('actioncomplete', this, action);
41297             
41298         }else{
41299             Roo.callback(o.failure, o.scope, [this, action]);
41300             // show an error message if no failed handler is set..
41301             if (!this.hasListener('actionfailed')) {
41302                 Roo.MessageBox.alert("Error",
41303                     typeof(action.result.errorMsg) != 'undefined' ?
41304                         action.result.errorMsg :
41305                         "Saving Failed, please check your entries"
41306                 );
41307             }
41308             
41309             this.fireEvent('actionfailed', this, action);
41310         }
41311         
41312     },
41313
41314     /**
41315      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41316      * @param {String} id The value to search for
41317      * @return Field
41318      */
41319     findField : function(id){
41320         var field = this.items.get(id);
41321         if(!field){
41322             this.items.each(function(f){
41323                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41324                     field = f;
41325                     return false;
41326                 }
41327             });
41328         }
41329         return field || null;
41330     },
41331
41332     /**
41333      * Add a secondary form to this one, 
41334      * Used to provide tabbed forms. One form is primary, with hidden values 
41335      * which mirror the elements from the other forms.
41336      * 
41337      * @param {Roo.form.Form} form to add.
41338      * 
41339      */
41340     addForm : function(form)
41341     {
41342        
41343         if (this.childForms.indexOf(form) > -1) {
41344             // already added..
41345             return;
41346         }
41347         this.childForms.push(form);
41348         var n = '';
41349         Roo.each(form.allItems, function (fe) {
41350             
41351             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41352             if (this.findField(n)) { // already added..
41353                 return;
41354             }
41355             var add = new Roo.form.Hidden({
41356                 name : n
41357             });
41358             add.render(this.el);
41359             
41360             this.add( add );
41361         }, this);
41362         
41363     },
41364     /**
41365      * Mark fields in this form invalid in bulk.
41366      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41367      * @return {BasicForm} this
41368      */
41369     markInvalid : function(errors){
41370         if(errors instanceof Array){
41371             for(var i = 0, len = errors.length; i < len; i++){
41372                 var fieldError = errors[i];
41373                 var f = this.findField(fieldError.id);
41374                 if(f){
41375                     f.markInvalid(fieldError.msg);
41376                 }
41377             }
41378         }else{
41379             var field, id;
41380             for(id in errors){
41381                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41382                     field.markInvalid(errors[id]);
41383                 }
41384             }
41385         }
41386         Roo.each(this.childForms || [], function (f) {
41387             f.markInvalid(errors);
41388         });
41389         
41390         return this;
41391     },
41392
41393     /**
41394      * Set values for fields in this form in bulk.
41395      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41396      * @return {BasicForm} this
41397      */
41398     setValues : function(values){
41399         if(values instanceof Array){ // array of objects
41400             for(var i = 0, len = values.length; i < len; i++){
41401                 var v = values[i];
41402                 var f = this.findField(v.id);
41403                 if(f){
41404                     f.setValue(v.value);
41405                     if(this.trackResetOnLoad){
41406                         f.originalValue = f.getValue();
41407                     }
41408                 }
41409             }
41410         }else{ // object hash
41411             var field, id;
41412             for(id in values){
41413                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41414                     
41415                     if (field.setFromData && 
41416                         field.valueField && 
41417                         field.displayField &&
41418                         // combos' with local stores can 
41419                         // be queried via setValue()
41420                         // to set their value..
41421                         (field.store && !field.store.isLocal)
41422                         ) {
41423                         // it's a combo
41424                         var sd = { };
41425                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41426                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41427                         field.setFromData(sd);
41428                         
41429                     } else {
41430                         field.setValue(values[id]);
41431                     }
41432                     
41433                     
41434                     if(this.trackResetOnLoad){
41435                         field.originalValue = field.getValue();
41436                     }
41437                 }
41438             }
41439         }
41440          
41441         Roo.each(this.childForms || [], function (f) {
41442             f.setValues(values);
41443         });
41444                 
41445         return this;
41446     },
41447
41448     /**
41449      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41450      * they are returned as an array.
41451      * @param {Boolean} asString
41452      * @return {Object}
41453      */
41454     getValues : function(asString){
41455         if (this.childForms) {
41456             // copy values from the child forms
41457             Roo.each(this.childForms, function (f) {
41458                 this.setValues(f.getValues());
41459             }, this);
41460         }
41461         
41462         
41463         
41464         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41465         if(asString === true){
41466             return fs;
41467         }
41468         return Roo.urlDecode(fs);
41469     },
41470     
41471     /**
41472      * Returns the fields in this form as an object with key/value pairs. 
41473      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41474      * @return {Object}
41475      */
41476     getFieldValues : function(with_hidden)
41477     {
41478         if (this.childForms) {
41479             // copy values from the child forms
41480             // should this call getFieldValues - probably not as we do not currently copy
41481             // hidden fields when we generate..
41482             Roo.each(this.childForms, function (f) {
41483                 this.setValues(f.getValues());
41484             }, this);
41485         }
41486         
41487         var ret = {};
41488         this.items.each(function(f){
41489             if (!f.getName()) {
41490                 return;
41491             }
41492             var v = f.getValue();
41493             // not sure if this supported any more..
41494             if ((typeof(v) == 'object') && f.getRawValue) {
41495                 v = f.getRawValue() ; // dates..
41496             }
41497             // combo boxes where name != hiddenName...
41498             if (f.name != f.getName()) {
41499                 ret[f.name] = f.getRawValue();
41500             }
41501             ret[f.getName()] = v;
41502         });
41503         
41504         return ret;
41505     },
41506
41507     /**
41508      * Clears all invalid messages in this form.
41509      * @return {BasicForm} this
41510      */
41511     clearInvalid : function(){
41512         this.items.each(function(f){
41513            f.clearInvalid();
41514         });
41515         
41516         Roo.each(this.childForms || [], function (f) {
41517             f.clearInvalid();
41518         });
41519         
41520         
41521         return this;
41522     },
41523
41524     /**
41525      * Resets this form.
41526      * @return {BasicForm} this
41527      */
41528     reset : function(){
41529         this.items.each(function(f){
41530             f.reset();
41531         });
41532         
41533         Roo.each(this.childForms || [], function (f) {
41534             f.reset();
41535         });
41536        
41537         
41538         return this;
41539     },
41540
41541     /**
41542      * Add Roo.form components to this form.
41543      * @param {Field} field1
41544      * @param {Field} field2 (optional)
41545      * @param {Field} etc (optional)
41546      * @return {BasicForm} this
41547      */
41548     add : function(){
41549         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41550         return this;
41551     },
41552
41553
41554     /**
41555      * Removes a field from the items collection (does NOT remove its markup).
41556      * @param {Field} field
41557      * @return {BasicForm} this
41558      */
41559     remove : function(field){
41560         this.items.remove(field);
41561         return this;
41562     },
41563
41564     /**
41565      * Looks at the fields in this form, checks them for an id attribute,
41566      * and calls applyTo on the existing dom element with that id.
41567      * @return {BasicForm} this
41568      */
41569     render : function(){
41570         this.items.each(function(f){
41571             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41572                 f.applyTo(f.id);
41573             }
41574         });
41575         return this;
41576     },
41577
41578     /**
41579      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41580      * @param {Object} values
41581      * @return {BasicForm} this
41582      */
41583     applyToFields : function(o){
41584         this.items.each(function(f){
41585            Roo.apply(f, o);
41586         });
41587         return this;
41588     },
41589
41590     /**
41591      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41592      * @param {Object} values
41593      * @return {BasicForm} this
41594      */
41595     applyIfToFields : function(o){
41596         this.items.each(function(f){
41597            Roo.applyIf(f, o);
41598         });
41599         return this;
41600     }
41601 });
41602
41603 // back compat
41604 Roo.BasicForm = Roo.form.BasicForm;/*
41605  * Based on:
41606  * Ext JS Library 1.1.1
41607  * Copyright(c) 2006-2007, Ext JS, LLC.
41608  *
41609  * Originally Released Under LGPL - original licence link has changed is not relivant.
41610  *
41611  * Fork - LGPL
41612  * <script type="text/javascript">
41613  */
41614
41615 /**
41616  * @class Roo.form.Form
41617  * @extends Roo.form.BasicForm
41618  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41619  * @constructor
41620  * @param {Object} config Configuration options
41621  */
41622 Roo.form.Form = function(config){
41623     var xitems =  [];
41624     if (config.items) {
41625         xitems = config.items;
41626         delete config.items;
41627     }
41628    
41629     
41630     Roo.form.Form.superclass.constructor.call(this, null, config);
41631     this.url = this.url || this.action;
41632     if(!this.root){
41633         this.root = new Roo.form.Layout(Roo.applyIf({
41634             id: Roo.id()
41635         }, config));
41636     }
41637     this.active = this.root;
41638     /**
41639      * Array of all the buttons that have been added to this form via {@link addButton}
41640      * @type Array
41641      */
41642     this.buttons = [];
41643     this.allItems = [];
41644     this.addEvents({
41645         /**
41646          * @event clientvalidation
41647          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41648          * @param {Form} this
41649          * @param {Boolean} valid true if the form has passed client-side validation
41650          */
41651         clientvalidation: true,
41652         /**
41653          * @event rendered
41654          * Fires when the form is rendered
41655          * @param {Roo.form.Form} form
41656          */
41657         rendered : true
41658     });
41659     
41660     if (this.progressUrl) {
41661             // push a hidden field onto the list of fields..
41662             this.addxtype( {
41663                     xns: Roo.form, 
41664                     xtype : 'Hidden', 
41665                     name : 'UPLOAD_IDENTIFIER' 
41666             });
41667         }
41668         
41669     
41670     Roo.each(xitems, this.addxtype, this);
41671     
41672     
41673     
41674 };
41675
41676 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41677     /**
41678      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41679      */
41680     /**
41681      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41682      */
41683     /**
41684      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41685      */
41686     buttonAlign:'center',
41687
41688     /**
41689      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41690      */
41691     minButtonWidth:75,
41692
41693     /**
41694      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41695      * This property cascades to child containers if not set.
41696      */
41697     labelAlign:'left',
41698
41699     /**
41700      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41701      * fires a looping event with that state. This is required to bind buttons to the valid
41702      * state using the config value formBind:true on the button.
41703      */
41704     monitorValid : false,
41705
41706     /**
41707      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41708      */
41709     monitorPoll : 200,
41710     
41711     /**
41712      * @cfg {String} progressUrl - Url to return progress data 
41713      */
41714     
41715     progressUrl : false,
41716   
41717     /**
41718      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41719      * fields are added and the column is closed. If no fields are passed the column remains open
41720      * until end() is called.
41721      * @param {Object} config The config to pass to the column
41722      * @param {Field} field1 (optional)
41723      * @param {Field} field2 (optional)
41724      * @param {Field} etc (optional)
41725      * @return Column The column container object
41726      */
41727     column : function(c){
41728         var col = new Roo.form.Column(c);
41729         this.start(col);
41730         if(arguments.length > 1){ // duplicate code required because of Opera
41731             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41732             this.end();
41733         }
41734         return col;
41735     },
41736
41737     /**
41738      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41739      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41740      * until end() is called.
41741      * @param {Object} config The config to pass to the fieldset
41742      * @param {Field} field1 (optional)
41743      * @param {Field} field2 (optional)
41744      * @param {Field} etc (optional)
41745      * @return FieldSet The fieldset container object
41746      */
41747     fieldset : function(c){
41748         var fs = new Roo.form.FieldSet(c);
41749         this.start(fs);
41750         if(arguments.length > 1){ // duplicate code required because of Opera
41751             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41752             this.end();
41753         }
41754         return fs;
41755     },
41756
41757     /**
41758      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41759      * fields are added and the container is closed. If no fields are passed the container remains open
41760      * until end() is called.
41761      * @param {Object} config The config to pass to the Layout
41762      * @param {Field} field1 (optional)
41763      * @param {Field} field2 (optional)
41764      * @param {Field} etc (optional)
41765      * @return Layout The container object
41766      */
41767     container : function(c){
41768         var l = new Roo.form.Layout(c);
41769         this.start(l);
41770         if(arguments.length > 1){ // duplicate code required because of Opera
41771             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41772             this.end();
41773         }
41774         return l;
41775     },
41776
41777     /**
41778      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41779      * @param {Object} container A Roo.form.Layout or subclass of Layout
41780      * @return {Form} this
41781      */
41782     start : function(c){
41783         // cascade label info
41784         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41785         this.active.stack.push(c);
41786         c.ownerCt = this.active;
41787         this.active = c;
41788         return this;
41789     },
41790
41791     /**
41792      * Closes the current open container
41793      * @return {Form} this
41794      */
41795     end : function(){
41796         if(this.active == this.root){
41797             return this;
41798         }
41799         this.active = this.active.ownerCt;
41800         return this;
41801     },
41802
41803     /**
41804      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41805      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41806      * as the label of the field.
41807      * @param {Field} field1
41808      * @param {Field} field2 (optional)
41809      * @param {Field} etc. (optional)
41810      * @return {Form} this
41811      */
41812     add : function(){
41813         this.active.stack.push.apply(this.active.stack, arguments);
41814         this.allItems.push.apply(this.allItems,arguments);
41815         var r = [];
41816         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41817             if(a[i].isFormField){
41818                 r.push(a[i]);
41819             }
41820         }
41821         if(r.length > 0){
41822             Roo.form.Form.superclass.add.apply(this, r);
41823         }
41824         return this;
41825     },
41826     
41827
41828     
41829     
41830     
41831      /**
41832      * Find any element that has been added to a form, using it's ID or name
41833      * This can include framesets, columns etc. along with regular fields..
41834      * @param {String} id - id or name to find.
41835      
41836      * @return {Element} e - or false if nothing found.
41837      */
41838     findbyId : function(id)
41839     {
41840         var ret = false;
41841         if (!id) {
41842             return ret;
41843         }
41844         Roo.each(this.allItems, function(f){
41845             if (f.id == id || f.name == id ){
41846                 ret = f;
41847                 return false;
41848             }
41849         });
41850         return ret;
41851     },
41852
41853     
41854     
41855     /**
41856      * Render this form into the passed container. This should only be called once!
41857      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41858      * @return {Form} this
41859      */
41860     render : function(ct)
41861     {
41862         
41863         
41864         
41865         ct = Roo.get(ct);
41866         var o = this.autoCreate || {
41867             tag: 'form',
41868             method : this.method || 'POST',
41869             id : this.id || Roo.id()
41870         };
41871         this.initEl(ct.createChild(o));
41872
41873         this.root.render(this.el);
41874         
41875        
41876              
41877         this.items.each(function(f){
41878             f.render('x-form-el-'+f.id);
41879         });
41880
41881         if(this.buttons.length > 0){
41882             // tables are required to maintain order and for correct IE layout
41883             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41884                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41885                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41886             }}, null, true);
41887             var tr = tb.getElementsByTagName('tr')[0];
41888             for(var i = 0, len = this.buttons.length; i < len; i++) {
41889                 var b = this.buttons[i];
41890                 var td = document.createElement('td');
41891                 td.className = 'x-form-btn-td';
41892                 b.render(tr.appendChild(td));
41893             }
41894         }
41895         if(this.monitorValid){ // initialize after render
41896             this.startMonitoring();
41897         }
41898         this.fireEvent('rendered', this);
41899         return this;
41900     },
41901
41902     /**
41903      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41904      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41905      * object or a valid Roo.DomHelper element config
41906      * @param {Function} handler The function called when the button is clicked
41907      * @param {Object} scope (optional) The scope of the handler function
41908      * @return {Roo.Button}
41909      */
41910     addButton : function(config, handler, scope){
41911         var bc = {
41912             handler: handler,
41913             scope: scope,
41914             minWidth: this.minButtonWidth,
41915             hideParent:true
41916         };
41917         if(typeof config == "string"){
41918             bc.text = config;
41919         }else{
41920             Roo.apply(bc, config);
41921         }
41922         var btn = new Roo.Button(null, bc);
41923         this.buttons.push(btn);
41924         return btn;
41925     },
41926
41927      /**
41928      * Adds a series of form elements (using the xtype property as the factory method.
41929      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41930      * @param {Object} config 
41931      */
41932     
41933     addxtype : function()
41934     {
41935         var ar = Array.prototype.slice.call(arguments, 0);
41936         var ret = false;
41937         for(var i = 0; i < ar.length; i++) {
41938             if (!ar[i]) {
41939                 continue; // skip -- if this happends something invalid got sent, we 
41940                 // should ignore it, as basically that interface element will not show up
41941                 // and that should be pretty obvious!!
41942             }
41943             
41944             if (Roo.form[ar[i].xtype]) {
41945                 ar[i].form = this;
41946                 var fe = Roo.factory(ar[i], Roo.form);
41947                 if (!ret) {
41948                     ret = fe;
41949                 }
41950                 fe.form = this;
41951                 if (fe.store) {
41952                     fe.store.form = this;
41953                 }
41954                 if (fe.isLayout) {  
41955                          
41956                     this.start(fe);
41957                     this.allItems.push(fe);
41958                     if (fe.items && fe.addxtype) {
41959                         fe.addxtype.apply(fe, fe.items);
41960                         delete fe.items;
41961                     }
41962                      this.end();
41963                     continue;
41964                 }
41965                 
41966                 
41967                  
41968                 this.add(fe);
41969               //  console.log('adding ' + ar[i].xtype);
41970             }
41971             if (ar[i].xtype == 'Button') {  
41972                 //console.log('adding button');
41973                 //console.log(ar[i]);
41974                 this.addButton(ar[i]);
41975                 this.allItems.push(fe);
41976                 continue;
41977             }
41978             
41979             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41980                 alert('end is not supported on xtype any more, use items');
41981             //    this.end();
41982             //    //console.log('adding end');
41983             }
41984             
41985         }
41986         return ret;
41987     },
41988     
41989     /**
41990      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41991      * option "monitorValid"
41992      */
41993     startMonitoring : function(){
41994         if(!this.bound){
41995             this.bound = true;
41996             Roo.TaskMgr.start({
41997                 run : this.bindHandler,
41998                 interval : this.monitorPoll || 200,
41999                 scope: this
42000             });
42001         }
42002     },
42003
42004     /**
42005      * Stops monitoring of the valid state of this form
42006      */
42007     stopMonitoring : function(){
42008         this.bound = false;
42009     },
42010
42011     // private
42012     bindHandler : function(){
42013         if(!this.bound){
42014             return false; // stops binding
42015         }
42016         var valid = true;
42017         this.items.each(function(f){
42018             if(!f.isValid(true)){
42019                 valid = false;
42020                 return false;
42021             }
42022         });
42023         for(var i = 0, len = this.buttons.length; i < len; i++){
42024             var btn = this.buttons[i];
42025             if(btn.formBind === true && btn.disabled === valid){
42026                 btn.setDisabled(!valid);
42027             }
42028         }
42029         this.fireEvent('clientvalidation', this, valid);
42030     }
42031     
42032     
42033     
42034     
42035     
42036     
42037     
42038     
42039 });
42040
42041
42042 // back compat
42043 Roo.Form = Roo.form.Form;
42044 /*
42045  * Based on:
42046  * Ext JS Library 1.1.1
42047  * Copyright(c) 2006-2007, Ext JS, LLC.
42048  *
42049  * Originally Released Under LGPL - original licence link has changed is not relivant.
42050  *
42051  * Fork - LGPL
42052  * <script type="text/javascript">
42053  */
42054  
42055  /**
42056  * @class Roo.form.Action
42057  * Internal Class used to handle form actions
42058  * @constructor
42059  * @param {Roo.form.BasicForm} el The form element or its id
42060  * @param {Object} config Configuration options
42061  */
42062  
42063  
42064 // define the action interface
42065 Roo.form.Action = function(form, options){
42066     this.form = form;
42067     this.options = options || {};
42068 };
42069 /**
42070  * Client Validation Failed
42071  * @const 
42072  */
42073 Roo.form.Action.CLIENT_INVALID = 'client';
42074 /**
42075  * Server Validation Failed
42076  * @const 
42077  */
42078  Roo.form.Action.SERVER_INVALID = 'server';
42079  /**
42080  * Connect to Server Failed
42081  * @const 
42082  */
42083 Roo.form.Action.CONNECT_FAILURE = 'connect';
42084 /**
42085  * Reading Data from Server Failed
42086  * @const 
42087  */
42088 Roo.form.Action.LOAD_FAILURE = 'load';
42089
42090 Roo.form.Action.prototype = {
42091     type : 'default',
42092     failureType : undefined,
42093     response : undefined,
42094     result : undefined,
42095
42096     // interface method
42097     run : function(options){
42098
42099     },
42100
42101     // interface method
42102     success : function(response){
42103
42104     },
42105
42106     // interface method
42107     handleResponse : function(response){
42108
42109     },
42110
42111     // default connection failure
42112     failure : function(response){
42113         
42114         this.response = response;
42115         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42116         this.form.afterAction(this, false);
42117     },
42118
42119     processResponse : function(response){
42120         this.response = response;
42121         if(!response.responseText){
42122             return true;
42123         }
42124         this.result = this.handleResponse(response);
42125         return this.result;
42126     },
42127
42128     // utility functions used internally
42129     getUrl : function(appendParams){
42130         var url = this.options.url || this.form.url || this.form.el.dom.action;
42131         if(appendParams){
42132             var p = this.getParams();
42133             if(p){
42134                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42135             }
42136         }
42137         return url;
42138     },
42139
42140     getMethod : function(){
42141         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42142     },
42143
42144     getParams : function(){
42145         var bp = this.form.baseParams;
42146         var p = this.options.params;
42147         if(p){
42148             if(typeof p == "object"){
42149                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42150             }else if(typeof p == 'string' && bp){
42151                 p += '&' + Roo.urlEncode(bp);
42152             }
42153         }else if(bp){
42154             p = Roo.urlEncode(bp);
42155         }
42156         return p;
42157     },
42158
42159     createCallback : function(){
42160         return {
42161             success: this.success,
42162             failure: this.failure,
42163             scope: this,
42164             timeout: (this.form.timeout*1000),
42165             upload: this.form.fileUpload ? this.success : undefined
42166         };
42167     }
42168 };
42169
42170 Roo.form.Action.Submit = function(form, options){
42171     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42172 };
42173
42174 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42175     type : 'submit',
42176
42177     haveProgress : false,
42178     uploadComplete : false,
42179     
42180     // uploadProgress indicator.
42181     uploadProgress : function()
42182     {
42183         if (!this.form.progressUrl) {
42184             return;
42185         }
42186         
42187         if (!this.haveProgress) {
42188             Roo.MessageBox.progress("Uploading", "Uploading");
42189         }
42190         if (this.uploadComplete) {
42191            Roo.MessageBox.hide();
42192            return;
42193         }
42194         
42195         this.haveProgress = true;
42196    
42197         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42198         
42199         var c = new Roo.data.Connection();
42200         c.request({
42201             url : this.form.progressUrl,
42202             params: {
42203                 id : uid
42204             },
42205             method: 'GET',
42206             success : function(req){
42207                //console.log(data);
42208                 var rdata = false;
42209                 var edata;
42210                 try  {
42211                    rdata = Roo.decode(req.responseText)
42212                 } catch (e) {
42213                     Roo.log("Invalid data from server..");
42214                     Roo.log(edata);
42215                     return;
42216                 }
42217                 if (!rdata || !rdata.success) {
42218                     Roo.log(rdata);
42219                     return;
42220                 }
42221                 var data = rdata.data;
42222                 
42223                 if (this.uploadComplete) {
42224                    Roo.MessageBox.hide();
42225                    return;
42226                 }
42227                    
42228                 if (data){
42229                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42230                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42231                     );
42232                 }
42233                 this.uploadProgress.defer(2000,this);
42234             },
42235        
42236             failure: function(data) {
42237                 Roo.log('progress url failed ');
42238                 Roo.log(data);
42239             },
42240             scope : this
42241         });
42242            
42243     },
42244     
42245     
42246     run : function()
42247     {
42248         // run get Values on the form, so it syncs any secondary forms.
42249         this.form.getValues();
42250         
42251         var o = this.options;
42252         var method = this.getMethod();
42253         var isPost = method == 'POST';
42254         if(o.clientValidation === false || this.form.isValid()){
42255             
42256             if (this.form.progressUrl) {
42257                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42258                     (new Date() * 1) + '' + Math.random());
42259                     
42260             } 
42261             
42262             
42263             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42264                 form:this.form.el.dom,
42265                 url:this.getUrl(!isPost),
42266                 method: method,
42267                 params:isPost ? this.getParams() : null,
42268                 isUpload: this.form.fileUpload
42269             }));
42270             
42271             this.uploadProgress();
42272
42273         }else if (o.clientValidation !== false){ // client validation failed
42274             this.failureType = Roo.form.Action.CLIENT_INVALID;
42275             this.form.afterAction(this, false);
42276         }
42277     },
42278
42279     success : function(response)
42280     {
42281         this.uploadComplete= true;
42282         if (this.haveProgress) {
42283             Roo.MessageBox.hide();
42284         }
42285         
42286         
42287         var result = this.processResponse(response);
42288         if(result === true || result.success){
42289             this.form.afterAction(this, true);
42290             return;
42291         }
42292         if(result.errors){
42293             this.form.markInvalid(result.errors);
42294             this.failureType = Roo.form.Action.SERVER_INVALID;
42295         }
42296         this.form.afterAction(this, false);
42297     },
42298     failure : function(response)
42299     {
42300         this.uploadComplete= true;
42301         if (this.haveProgress) {
42302             Roo.MessageBox.hide();
42303         }
42304         
42305         this.response = response;
42306         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42307         this.form.afterAction(this, false);
42308     },
42309     
42310     handleResponse : function(response){
42311         if(this.form.errorReader){
42312             var rs = this.form.errorReader.read(response);
42313             var errors = [];
42314             if(rs.records){
42315                 for(var i = 0, len = rs.records.length; i < len; i++) {
42316                     var r = rs.records[i];
42317                     errors[i] = r.data;
42318                 }
42319             }
42320             if(errors.length < 1){
42321                 errors = null;
42322             }
42323             return {
42324                 success : rs.success,
42325                 errors : errors
42326             };
42327         }
42328         var ret = false;
42329         try {
42330             ret = Roo.decode(response.responseText);
42331         } catch (e) {
42332             ret = {
42333                 success: false,
42334                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42335                 errors : []
42336             };
42337         }
42338         return ret;
42339         
42340     }
42341 });
42342
42343
42344 Roo.form.Action.Load = function(form, options){
42345     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42346     this.reader = this.form.reader;
42347 };
42348
42349 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42350     type : 'load',
42351
42352     run : function(){
42353         
42354         Roo.Ajax.request(Roo.apply(
42355                 this.createCallback(), {
42356                     method:this.getMethod(),
42357                     url:this.getUrl(false),
42358                     params:this.getParams()
42359         }));
42360     },
42361
42362     success : function(response){
42363         
42364         var result = this.processResponse(response);
42365         if(result === true || !result.success || !result.data){
42366             this.failureType = Roo.form.Action.LOAD_FAILURE;
42367             this.form.afterAction(this, false);
42368             return;
42369         }
42370         this.form.clearInvalid();
42371         this.form.setValues(result.data);
42372         this.form.afterAction(this, true);
42373     },
42374
42375     handleResponse : function(response){
42376         if(this.form.reader){
42377             var rs = this.form.reader.read(response);
42378             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42379             return {
42380                 success : rs.success,
42381                 data : data
42382             };
42383         }
42384         return Roo.decode(response.responseText);
42385     }
42386 });
42387
42388 Roo.form.Action.ACTION_TYPES = {
42389     'load' : Roo.form.Action.Load,
42390     'submit' : Roo.form.Action.Submit
42391 };/*
42392  * Based on:
42393  * Ext JS Library 1.1.1
42394  * Copyright(c) 2006-2007, Ext JS, LLC.
42395  *
42396  * Originally Released Under LGPL - original licence link has changed is not relivant.
42397  *
42398  * Fork - LGPL
42399  * <script type="text/javascript">
42400  */
42401  
42402 /**
42403  * @class Roo.form.Layout
42404  * @extends Roo.Component
42405  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42406  * @constructor
42407  * @param {Object} config Configuration options
42408  */
42409 Roo.form.Layout = function(config){
42410     var xitems = [];
42411     if (config.items) {
42412         xitems = config.items;
42413         delete config.items;
42414     }
42415     Roo.form.Layout.superclass.constructor.call(this, config);
42416     this.stack = [];
42417     Roo.each(xitems, this.addxtype, this);
42418      
42419 };
42420
42421 Roo.extend(Roo.form.Layout, Roo.Component, {
42422     /**
42423      * @cfg {String/Object} autoCreate
42424      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42425      */
42426     /**
42427      * @cfg {String/Object/Function} style
42428      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42429      * a function which returns such a specification.
42430      */
42431     /**
42432      * @cfg {String} labelAlign
42433      * Valid values are "left," "top" and "right" (defaults to "left")
42434      */
42435     /**
42436      * @cfg {Number} labelWidth
42437      * Fixed width in pixels of all field labels (defaults to undefined)
42438      */
42439     /**
42440      * @cfg {Boolean} clear
42441      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42442      */
42443     clear : true,
42444     /**
42445      * @cfg {String} labelSeparator
42446      * The separator to use after field labels (defaults to ':')
42447      */
42448     labelSeparator : ':',
42449     /**
42450      * @cfg {Boolean} hideLabels
42451      * True to suppress the display of field labels in this layout (defaults to false)
42452      */
42453     hideLabels : false,
42454
42455     // private
42456     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42457     
42458     isLayout : true,
42459     
42460     // private
42461     onRender : function(ct, position){
42462         if(this.el){ // from markup
42463             this.el = Roo.get(this.el);
42464         }else {  // generate
42465             var cfg = this.getAutoCreate();
42466             this.el = ct.createChild(cfg, position);
42467         }
42468         if(this.style){
42469             this.el.applyStyles(this.style);
42470         }
42471         if(this.labelAlign){
42472             this.el.addClass('x-form-label-'+this.labelAlign);
42473         }
42474         if(this.hideLabels){
42475             this.labelStyle = "display:none";
42476             this.elementStyle = "padding-left:0;";
42477         }else{
42478             if(typeof this.labelWidth == 'number'){
42479                 this.labelStyle = "width:"+this.labelWidth+"px;";
42480                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42481             }
42482             if(this.labelAlign == 'top'){
42483                 this.labelStyle = "width:auto;";
42484                 this.elementStyle = "padding-left:0;";
42485             }
42486         }
42487         var stack = this.stack;
42488         var slen = stack.length;
42489         if(slen > 0){
42490             if(!this.fieldTpl){
42491                 var t = new Roo.Template(
42492                     '<div class="x-form-item {5}">',
42493                         '<label for="{0}" style="{2}">{1}{4}</label>',
42494                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42495                         '</div>',
42496                     '</div><div class="x-form-clear-left"></div>'
42497                 );
42498                 t.disableFormats = true;
42499                 t.compile();
42500                 Roo.form.Layout.prototype.fieldTpl = t;
42501             }
42502             for(var i = 0; i < slen; i++) {
42503                 if(stack[i].isFormField){
42504                     this.renderField(stack[i]);
42505                 }else{
42506                     this.renderComponent(stack[i]);
42507                 }
42508             }
42509         }
42510         if(this.clear){
42511             this.el.createChild({cls:'x-form-clear'});
42512         }
42513     },
42514
42515     // private
42516     renderField : function(f){
42517         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42518                f.id, //0
42519                f.fieldLabel, //1
42520                f.labelStyle||this.labelStyle||'', //2
42521                this.elementStyle||'', //3
42522                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42523                f.itemCls||this.itemCls||''  //5
42524        ], true).getPrevSibling());
42525     },
42526
42527     // private
42528     renderComponent : function(c){
42529         c.render(c.isLayout ? this.el : this.el.createChild());    
42530     },
42531     /**
42532      * Adds a object form elements (using the xtype property as the factory method.)
42533      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42534      * @param {Object} config 
42535      */
42536     addxtype : function(o)
42537     {
42538         // create the lement.
42539         o.form = this.form;
42540         var fe = Roo.factory(o, Roo.form);
42541         this.form.allItems.push(fe);
42542         this.stack.push(fe);
42543         
42544         if (fe.isFormField) {
42545             this.form.items.add(fe);
42546         }
42547          
42548         return fe;
42549     }
42550 });
42551
42552 /**
42553  * @class Roo.form.Column
42554  * @extends Roo.form.Layout
42555  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42556  * @constructor
42557  * @param {Object} config Configuration options
42558  */
42559 Roo.form.Column = function(config){
42560     Roo.form.Column.superclass.constructor.call(this, config);
42561 };
42562
42563 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42564     /**
42565      * @cfg {Number/String} width
42566      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42567      */
42568     /**
42569      * @cfg {String/Object} autoCreate
42570      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42571      */
42572
42573     // private
42574     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42575
42576     // private
42577     onRender : function(ct, position){
42578         Roo.form.Column.superclass.onRender.call(this, ct, position);
42579         if(this.width){
42580             this.el.setWidth(this.width);
42581         }
42582     }
42583 });
42584
42585
42586 /**
42587  * @class Roo.form.Row
42588  * @extends Roo.form.Layout
42589  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42590  * @constructor
42591  * @param {Object} config Configuration options
42592  */
42593
42594  
42595 Roo.form.Row = function(config){
42596     Roo.form.Row.superclass.constructor.call(this, config);
42597 };
42598  
42599 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42600       /**
42601      * @cfg {Number/String} width
42602      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42603      */
42604     /**
42605      * @cfg {Number/String} height
42606      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42607      */
42608     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42609     
42610     padWidth : 20,
42611     // private
42612     onRender : function(ct, position){
42613         //console.log('row render');
42614         if(!this.rowTpl){
42615             var t = new Roo.Template(
42616                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42617                     '<label for="{0}" style="{2}">{1}{4}</label>',
42618                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42619                     '</div>',
42620                 '</div>'
42621             );
42622             t.disableFormats = true;
42623             t.compile();
42624             Roo.form.Layout.prototype.rowTpl = t;
42625         }
42626         this.fieldTpl = this.rowTpl;
42627         
42628         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42629         var labelWidth = 100;
42630         
42631         if ((this.labelAlign != 'top')) {
42632             if (typeof this.labelWidth == 'number') {
42633                 labelWidth = this.labelWidth
42634             }
42635             this.padWidth =  20 + labelWidth;
42636             
42637         }
42638         
42639         Roo.form.Column.superclass.onRender.call(this, ct, position);
42640         if(this.width){
42641             this.el.setWidth(this.width);
42642         }
42643         if(this.height){
42644             this.el.setHeight(this.height);
42645         }
42646     },
42647     
42648     // private
42649     renderField : function(f){
42650         f.fieldEl = this.fieldTpl.append(this.el, [
42651                f.id, f.fieldLabel,
42652                f.labelStyle||this.labelStyle||'',
42653                this.elementStyle||'',
42654                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42655                f.itemCls||this.itemCls||'',
42656                f.width ? f.width + this.padWidth : 160 + this.padWidth
42657        ],true);
42658     }
42659 });
42660  
42661
42662 /**
42663  * @class Roo.form.FieldSet
42664  * @extends Roo.form.Layout
42665  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42666  * @constructor
42667  * @param {Object} config Configuration options
42668  */
42669 Roo.form.FieldSet = function(config){
42670     Roo.form.FieldSet.superclass.constructor.call(this, config);
42671 };
42672
42673 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42674     /**
42675      * @cfg {String} legend
42676      * The text to display as the legend for the FieldSet (defaults to '')
42677      */
42678     /**
42679      * @cfg {String/Object} autoCreate
42680      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42681      */
42682
42683     // private
42684     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42685
42686     // private
42687     onRender : function(ct, position){
42688         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42689         if(this.legend){
42690             this.setLegend(this.legend);
42691         }
42692     },
42693
42694     // private
42695     setLegend : function(text){
42696         if(this.rendered){
42697             this.el.child('legend').update(text);
42698         }
42699     }
42700 });/*
42701  * Based on:
42702  * Ext JS Library 1.1.1
42703  * Copyright(c) 2006-2007, Ext JS, LLC.
42704  *
42705  * Originally Released Under LGPL - original licence link has changed is not relivant.
42706  *
42707  * Fork - LGPL
42708  * <script type="text/javascript">
42709  */
42710 /**
42711  * @class Roo.form.VTypes
42712  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42713  * @singleton
42714  */
42715 Roo.form.VTypes = function(){
42716     // closure these in so they are only created once.
42717     var alpha = /^[a-zA-Z_]+$/;
42718     var alphanum = /^[a-zA-Z0-9_]+$/;
42719     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42720     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42721
42722     // All these messages and functions are configurable
42723     return {
42724         /**
42725          * The function used to validate email addresses
42726          * @param {String} value The email address
42727          */
42728         'email' : function(v){
42729             return email.test(v);
42730         },
42731         /**
42732          * The error text to display when the email validation function returns false
42733          * @type String
42734          */
42735         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42736         /**
42737          * The keystroke filter mask to be applied on email input
42738          * @type RegExp
42739          */
42740         'emailMask' : /[a-z0-9_\.\-@]/i,
42741
42742         /**
42743          * The function used to validate URLs
42744          * @param {String} value The URL
42745          */
42746         'url' : function(v){
42747             return url.test(v);
42748         },
42749         /**
42750          * The error text to display when the url validation function returns false
42751          * @type String
42752          */
42753         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42754         
42755         /**
42756          * The function used to validate alpha values
42757          * @param {String} value The value
42758          */
42759         'alpha' : function(v){
42760             return alpha.test(v);
42761         },
42762         /**
42763          * The error text to display when the alpha validation function returns false
42764          * @type String
42765          */
42766         'alphaText' : 'This field should only contain letters and _',
42767         /**
42768          * The keystroke filter mask to be applied on alpha input
42769          * @type RegExp
42770          */
42771         'alphaMask' : /[a-z_]/i,
42772
42773         /**
42774          * The function used to validate alphanumeric values
42775          * @param {String} value The value
42776          */
42777         'alphanum' : function(v){
42778             return alphanum.test(v);
42779         },
42780         /**
42781          * The error text to display when the alphanumeric validation function returns false
42782          * @type String
42783          */
42784         'alphanumText' : 'This field should only contain letters, numbers and _',
42785         /**
42786          * The keystroke filter mask to be applied on alphanumeric input
42787          * @type RegExp
42788          */
42789         'alphanumMask' : /[a-z0-9_]/i
42790     };
42791 }();//<script type="text/javascript">
42792
42793 /**
42794  * @class Roo.form.FCKeditor
42795  * @extends Roo.form.TextArea
42796  * Wrapper around the FCKEditor http://www.fckeditor.net
42797  * @constructor
42798  * Creates a new FCKeditor
42799  * @param {Object} config Configuration options
42800  */
42801 Roo.form.FCKeditor = function(config){
42802     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42803     this.addEvents({
42804          /**
42805          * @event editorinit
42806          * Fired when the editor is initialized - you can add extra handlers here..
42807          * @param {FCKeditor} this
42808          * @param {Object} the FCK object.
42809          */
42810         editorinit : true
42811     });
42812     
42813     
42814 };
42815 Roo.form.FCKeditor.editors = { };
42816 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42817 {
42818     //defaultAutoCreate : {
42819     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42820     //},
42821     // private
42822     /**
42823      * @cfg {Object} fck options - see fck manual for details.
42824      */
42825     fckconfig : false,
42826     
42827     /**
42828      * @cfg {Object} fck toolbar set (Basic or Default)
42829      */
42830     toolbarSet : 'Basic',
42831     /**
42832      * @cfg {Object} fck BasePath
42833      */ 
42834     basePath : '/fckeditor/',
42835     
42836     
42837     frame : false,
42838     
42839     value : '',
42840     
42841    
42842     onRender : function(ct, position)
42843     {
42844         if(!this.el){
42845             this.defaultAutoCreate = {
42846                 tag: "textarea",
42847                 style:"width:300px;height:60px;",
42848                 autocomplete: "off"
42849             };
42850         }
42851         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42852         /*
42853         if(this.grow){
42854             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42855             if(this.preventScrollbars){
42856                 this.el.setStyle("overflow", "hidden");
42857             }
42858             this.el.setHeight(this.growMin);
42859         }
42860         */
42861         //console.log('onrender' + this.getId() );
42862         Roo.form.FCKeditor.editors[this.getId()] = this;
42863          
42864
42865         this.replaceTextarea() ;
42866         
42867     },
42868     
42869     getEditor : function() {
42870         return this.fckEditor;
42871     },
42872     /**
42873      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42874      * @param {Mixed} value The value to set
42875      */
42876     
42877     
42878     setValue : function(value)
42879     {
42880         //console.log('setValue: ' + value);
42881         
42882         if(typeof(value) == 'undefined') { // not sure why this is happending...
42883             return;
42884         }
42885         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42886         
42887         //if(!this.el || !this.getEditor()) {
42888         //    this.value = value;
42889             //this.setValue.defer(100,this,[value]);    
42890         //    return;
42891         //} 
42892         
42893         if(!this.getEditor()) {
42894             return;
42895         }
42896         
42897         this.getEditor().SetData(value);
42898         
42899         //
42900
42901     },
42902
42903     /**
42904      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42905      * @return {Mixed} value The field value
42906      */
42907     getValue : function()
42908     {
42909         
42910         if (this.frame && this.frame.dom.style.display == 'none') {
42911             return Roo.form.FCKeditor.superclass.getValue.call(this);
42912         }
42913         
42914         if(!this.el || !this.getEditor()) {
42915            
42916            // this.getValue.defer(100,this); 
42917             return this.value;
42918         }
42919        
42920         
42921         var value=this.getEditor().GetData();
42922         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42923         return Roo.form.FCKeditor.superclass.getValue.call(this);
42924         
42925
42926     },
42927
42928     /**
42929      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42930      * @return {Mixed} value The field value
42931      */
42932     getRawValue : function()
42933     {
42934         if (this.frame && this.frame.dom.style.display == 'none') {
42935             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42936         }
42937         
42938         if(!this.el || !this.getEditor()) {
42939             //this.getRawValue.defer(100,this); 
42940             return this.value;
42941             return;
42942         }
42943         
42944         
42945         
42946         var value=this.getEditor().GetData();
42947         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42948         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42949          
42950     },
42951     
42952     setSize : function(w,h) {
42953         
42954         
42955         
42956         //if (this.frame && this.frame.dom.style.display == 'none') {
42957         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42958         //    return;
42959         //}
42960         //if(!this.el || !this.getEditor()) {
42961         //    this.setSize.defer(100,this, [w,h]); 
42962         //    return;
42963         //}
42964         
42965         
42966         
42967         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42968         
42969         this.frame.dom.setAttribute('width', w);
42970         this.frame.dom.setAttribute('height', h);
42971         this.frame.setSize(w,h);
42972         
42973     },
42974     
42975     toggleSourceEdit : function(value) {
42976         
42977       
42978          
42979         this.el.dom.style.display = value ? '' : 'none';
42980         this.frame.dom.style.display = value ?  'none' : '';
42981         
42982     },
42983     
42984     
42985     focus: function(tag)
42986     {
42987         if (this.frame.dom.style.display == 'none') {
42988             return Roo.form.FCKeditor.superclass.focus.call(this);
42989         }
42990         if(!this.el || !this.getEditor()) {
42991             this.focus.defer(100,this, [tag]); 
42992             return;
42993         }
42994         
42995         
42996         
42997         
42998         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42999         this.getEditor().Focus();
43000         if (tgs.length) {
43001             if (!this.getEditor().Selection.GetSelection()) {
43002                 this.focus.defer(100,this, [tag]); 
43003                 return;
43004             }
43005             
43006             
43007             var r = this.getEditor().EditorDocument.createRange();
43008             r.setStart(tgs[0],0);
43009             r.setEnd(tgs[0],0);
43010             this.getEditor().Selection.GetSelection().removeAllRanges();
43011             this.getEditor().Selection.GetSelection().addRange(r);
43012             this.getEditor().Focus();
43013         }
43014         
43015     },
43016     
43017     
43018     
43019     replaceTextarea : function()
43020     {
43021         if ( document.getElementById( this.getId() + '___Frame' ) )
43022             return ;
43023         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43024         //{
43025             // We must check the elements firstly using the Id and then the name.
43026         var oTextarea = document.getElementById( this.getId() );
43027         
43028         var colElementsByName = document.getElementsByName( this.getId() ) ;
43029          
43030         oTextarea.style.display = 'none' ;
43031
43032         if ( oTextarea.tabIndex ) {            
43033             this.TabIndex = oTextarea.tabIndex ;
43034         }
43035         
43036         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43037         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43038         this.frame = Roo.get(this.getId() + '___Frame')
43039     },
43040     
43041     _getConfigHtml : function()
43042     {
43043         var sConfig = '' ;
43044
43045         for ( var o in this.fckconfig ) {
43046             sConfig += sConfig.length > 0  ? '&amp;' : '';
43047             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43048         }
43049
43050         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43051     },
43052     
43053     
43054     _getIFrameHtml : function()
43055     {
43056         var sFile = 'fckeditor.html' ;
43057         /* no idea what this is about..
43058         try
43059         {
43060             if ( (/fcksource=true/i).test( window.top.location.search ) )
43061                 sFile = 'fckeditor.original.html' ;
43062         }
43063         catch (e) { 
43064         */
43065
43066         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43067         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43068         
43069         
43070         var html = '<iframe id="' + this.getId() +
43071             '___Frame" src="' + sLink +
43072             '" width="' + this.width +
43073             '" height="' + this.height + '"' +
43074             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43075             ' frameborder="0" scrolling="no"></iframe>' ;
43076
43077         return html ;
43078     },
43079     
43080     _insertHtmlBefore : function( html, element )
43081     {
43082         if ( element.insertAdjacentHTML )       {
43083             // IE
43084             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43085         } else { // Gecko
43086             var oRange = document.createRange() ;
43087             oRange.setStartBefore( element ) ;
43088             var oFragment = oRange.createContextualFragment( html );
43089             element.parentNode.insertBefore( oFragment, element ) ;
43090         }
43091     }
43092     
43093     
43094   
43095     
43096     
43097     
43098     
43099
43100 });
43101
43102 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43103
43104 function FCKeditor_OnComplete(editorInstance){
43105     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43106     f.fckEditor = editorInstance;
43107     //console.log("loaded");
43108     f.fireEvent('editorinit', f, editorInstance);
43109
43110   
43111
43112  
43113
43114
43115
43116
43117
43118
43119
43120
43121
43122
43123
43124
43125
43126
43127
43128 //<script type="text/javascript">
43129 /**
43130  * @class Roo.form.GridField
43131  * @extends Roo.form.Field
43132  * Embed a grid (or editable grid into a form)
43133  * STATUS ALPHA
43134  * 
43135  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43136  * it needs 
43137  * xgrid.store = Roo.data.Store
43138  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43139  * xgrid.store.reader = Roo.data.JsonReader 
43140  * 
43141  * 
43142  * @constructor
43143  * Creates a new GridField
43144  * @param {Object} config Configuration options
43145  */
43146 Roo.form.GridField = function(config){
43147     Roo.form.GridField.superclass.constructor.call(this, config);
43148      
43149 };
43150
43151 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43152     /**
43153      * @cfg {Number} width  - used to restrict width of grid..
43154      */
43155     width : 100,
43156     /**
43157      * @cfg {Number} height - used to restrict height of grid..
43158      */
43159     height : 50,
43160      /**
43161      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43162          * 
43163          *}
43164      */
43165     xgrid : false, 
43166     /**
43167      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43168      * {tag: "input", type: "checkbox", autocomplete: "off"})
43169      */
43170    // defaultAutoCreate : { tag: 'div' },
43171     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43172     /**
43173      * @cfg {String} addTitle Text to include for adding a title.
43174      */
43175     addTitle : false,
43176     //
43177     onResize : function(){
43178         Roo.form.Field.superclass.onResize.apply(this, arguments);
43179     },
43180
43181     initEvents : function(){
43182         // Roo.form.Checkbox.superclass.initEvents.call(this);
43183         // has no events...
43184        
43185     },
43186
43187
43188     getResizeEl : function(){
43189         return this.wrap;
43190     },
43191
43192     getPositionEl : function(){
43193         return this.wrap;
43194     },
43195
43196     // private
43197     onRender : function(ct, position){
43198         
43199         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43200         var style = this.style;
43201         delete this.style;
43202         
43203         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43204         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43205         this.viewEl = this.wrap.createChild({ tag: 'div' });
43206         if (style) {
43207             this.viewEl.applyStyles(style);
43208         }
43209         if (this.width) {
43210             this.viewEl.setWidth(this.width);
43211         }
43212         if (this.height) {
43213             this.viewEl.setHeight(this.height);
43214         }
43215         //if(this.inputValue !== undefined){
43216         //this.setValue(this.value);
43217         
43218         
43219         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43220         
43221         
43222         this.grid.render();
43223         this.grid.getDataSource().on('remove', this.refreshValue, this);
43224         this.grid.getDataSource().on('update', this.refreshValue, this);
43225         this.grid.on('afteredit', this.refreshValue, this);
43226  
43227     },
43228      
43229     
43230     /**
43231      * Sets the value of the item. 
43232      * @param {String} either an object  or a string..
43233      */
43234     setValue : function(v){
43235         //this.value = v;
43236         v = v || []; // empty set..
43237         // this does not seem smart - it really only affects memoryproxy grids..
43238         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43239             var ds = this.grid.getDataSource();
43240             // assumes a json reader..
43241             var data = {}
43242             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43243             ds.loadData( data);
43244         }
43245         // clear selection so it does not get stale.
43246         if (this.grid.sm) { 
43247             this.grid.sm.clearSelections();
43248         }
43249         
43250         Roo.form.GridField.superclass.setValue.call(this, v);
43251         this.refreshValue();
43252         // should load data in the grid really....
43253     },
43254     
43255     // private
43256     refreshValue: function() {
43257          var val = [];
43258         this.grid.getDataSource().each(function(r) {
43259             val.push(r.data);
43260         });
43261         this.el.dom.value = Roo.encode(val);
43262     }
43263     
43264      
43265     
43266     
43267 });/*
43268  * Based on:
43269  * Ext JS Library 1.1.1
43270  * Copyright(c) 2006-2007, Ext JS, LLC.
43271  *
43272  * Originally Released Under LGPL - original licence link has changed is not relivant.
43273  *
43274  * Fork - LGPL
43275  * <script type="text/javascript">
43276  */
43277 /**
43278  * @class Roo.form.DisplayField
43279  * @extends Roo.form.Field
43280  * A generic Field to display non-editable data.
43281  * @constructor
43282  * Creates a new Display Field item.
43283  * @param {Object} config Configuration options
43284  */
43285 Roo.form.DisplayField = function(config){
43286     Roo.form.DisplayField.superclass.constructor.call(this, config);
43287     
43288 };
43289
43290 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43291     inputType:      'hidden',
43292     allowBlank:     true,
43293     readOnly:         true,
43294     
43295  
43296     /**
43297      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43298      */
43299     focusClass : undefined,
43300     /**
43301      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43302      */
43303     fieldClass: 'x-form-field',
43304     
43305      /**
43306      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43307      */
43308     valueRenderer: undefined,
43309     
43310     width: 100,
43311     /**
43312      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43313      * {tag: "input", type: "checkbox", autocomplete: "off"})
43314      */
43315      
43316  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43317
43318     onResize : function(){
43319         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43320         
43321     },
43322
43323     initEvents : function(){
43324         // Roo.form.Checkbox.superclass.initEvents.call(this);
43325         // has no events...
43326        
43327     },
43328
43329
43330     getResizeEl : function(){
43331         return this.wrap;
43332     },
43333
43334     getPositionEl : function(){
43335         return this.wrap;
43336     },
43337
43338     // private
43339     onRender : function(ct, position){
43340         
43341         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43342         //if(this.inputValue !== undefined){
43343         this.wrap = this.el.wrap();
43344         
43345         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43346         
43347         if (this.bodyStyle) {
43348             this.viewEl.applyStyles(this.bodyStyle);
43349         }
43350         //this.viewEl.setStyle('padding', '2px');
43351         
43352         this.setValue(this.value);
43353         
43354     },
43355 /*
43356     // private
43357     initValue : Roo.emptyFn,
43358
43359   */
43360
43361         // private
43362     onClick : function(){
43363         
43364     },
43365
43366     /**
43367      * Sets the checked state of the checkbox.
43368      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43369      */
43370     setValue : function(v){
43371         this.value = v;
43372         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43373         // this might be called before we have a dom element..
43374         if (!this.viewEl) {
43375             return;
43376         }
43377         this.viewEl.dom.innerHTML = html;
43378         Roo.form.DisplayField.superclass.setValue.call(this, v);
43379
43380     }
43381 });/*
43382  * 
43383  * Licence- LGPL
43384  * 
43385  */
43386
43387 /**
43388  * @class Roo.form.DayPicker
43389  * @extends Roo.form.Field
43390  * A Day picker show [M] [T] [W] ....
43391  * @constructor
43392  * Creates a new Day Picker
43393  * @param {Object} config Configuration options
43394  */
43395 Roo.form.DayPicker= function(config){
43396     Roo.form.DayPicker.superclass.constructor.call(this, config);
43397      
43398 };
43399
43400 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43401     /**
43402      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43403      */
43404     focusClass : undefined,
43405     /**
43406      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43407      */
43408     fieldClass: "x-form-field",
43409    
43410     /**
43411      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43412      * {tag: "input", type: "checkbox", autocomplete: "off"})
43413      */
43414     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43415     
43416    
43417     actionMode : 'viewEl', 
43418     //
43419     // private
43420  
43421     inputType : 'hidden',
43422     
43423      
43424     inputElement: false, // real input element?
43425     basedOn: false, // ????
43426     
43427     isFormField: true, // not sure where this is needed!!!!
43428
43429     onResize : function(){
43430         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43431         if(!this.boxLabel){
43432             this.el.alignTo(this.wrap, 'c-c');
43433         }
43434     },
43435
43436     initEvents : function(){
43437         Roo.form.Checkbox.superclass.initEvents.call(this);
43438         this.el.on("click", this.onClick,  this);
43439         this.el.on("change", this.onClick,  this);
43440     },
43441
43442
43443     getResizeEl : function(){
43444         return this.wrap;
43445     },
43446
43447     getPositionEl : function(){
43448         return this.wrap;
43449     },
43450
43451     
43452     // private
43453     onRender : function(ct, position){
43454         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43455        
43456         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43457         
43458         var r1 = '<table><tr>';
43459         var r2 = '<tr class="x-form-daypick-icons">';
43460         for (var i=0; i < 7; i++) {
43461             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43462             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43463         }
43464         
43465         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43466         viewEl.select('img').on('click', this.onClick, this);
43467         this.viewEl = viewEl;   
43468         
43469         
43470         // this will not work on Chrome!!!
43471         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43472         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43473         
43474         
43475           
43476
43477     },
43478
43479     // private
43480     initValue : Roo.emptyFn,
43481
43482     /**
43483      * Returns the checked state of the checkbox.
43484      * @return {Boolean} True if checked, else false
43485      */
43486     getValue : function(){
43487         return this.el.dom.value;
43488         
43489     },
43490
43491         // private
43492     onClick : function(e){ 
43493         //this.setChecked(!this.checked);
43494         Roo.get(e.target).toggleClass('x-menu-item-checked');
43495         this.refreshValue();
43496         //if(this.el.dom.checked != this.checked){
43497         //    this.setValue(this.el.dom.checked);
43498        // }
43499     },
43500     
43501     // private
43502     refreshValue : function()
43503     {
43504         var val = '';
43505         this.viewEl.select('img',true).each(function(e,i,n)  {
43506             val += e.is(".x-menu-item-checked") ? String(n) : '';
43507         });
43508         this.setValue(val, true);
43509     },
43510
43511     /**
43512      * Sets the checked state of the checkbox.
43513      * On is always based on a string comparison between inputValue and the param.
43514      * @param {Boolean/String} value - the value to set 
43515      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43516      */
43517     setValue : function(v,suppressEvent){
43518         if (!this.el.dom) {
43519             return;
43520         }
43521         var old = this.el.dom.value ;
43522         this.el.dom.value = v;
43523         if (suppressEvent) {
43524             return ;
43525         }
43526          
43527         // update display..
43528         this.viewEl.select('img',true).each(function(e,i,n)  {
43529             
43530             var on = e.is(".x-menu-item-checked");
43531             var newv = v.indexOf(String(n)) > -1;
43532             if (on != newv) {
43533                 e.toggleClass('x-menu-item-checked');
43534             }
43535             
43536         });
43537         
43538         
43539         this.fireEvent('change', this, v, old);
43540         
43541         
43542     },
43543    
43544     // handle setting of hidden value by some other method!!?!?
43545     setFromHidden: function()
43546     {
43547         if(!this.el){
43548             return;
43549         }
43550         //console.log("SET FROM HIDDEN");
43551         //alert('setFrom hidden');
43552         this.setValue(this.el.dom.value);
43553     },
43554     
43555     onDestroy : function()
43556     {
43557         if(this.viewEl){
43558             Roo.get(this.viewEl).remove();
43559         }
43560          
43561         Roo.form.DayPicker.superclass.onDestroy.call(this);
43562     }
43563
43564 });/*
43565  * RooJS Library 1.1.1
43566  * Copyright(c) 2008-2011  Alan Knowles
43567  *
43568  * License - LGPL
43569  */
43570  
43571
43572 /**
43573  * @class Roo.form.ComboCheck
43574  * @extends Roo.form.ComboBox
43575  * A combobox for multiple select items.
43576  *
43577  * FIXME - could do with a reset button..
43578  * 
43579  * @constructor
43580  * Create a new ComboCheck
43581  * @param {Object} config Configuration options
43582  */
43583 Roo.form.ComboCheck = function(config){
43584     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43585     // should verify some data...
43586     // like
43587     // hiddenName = required..
43588     // displayField = required
43589     // valudField == required
43590     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43591     var _t = this;
43592     Roo.each(req, function(e) {
43593         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43594             throw "Roo.form.ComboCheck : missing value for: " + e;
43595         }
43596     });
43597     
43598     
43599 };
43600
43601 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43602      
43603      
43604     editable : false,
43605      
43606     selectedClass: 'x-menu-item-checked', 
43607     
43608     // private
43609     onRender : function(ct, position){
43610         var _t = this;
43611         
43612         
43613         
43614         if(!this.tpl){
43615             var cls = 'x-combo-list';
43616
43617             
43618             this.tpl =  new Roo.Template({
43619                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43620                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43621                    '<span>{' + this.displayField + '}</span>' +
43622                     '</div>' 
43623                 
43624             });
43625         }
43626  
43627         
43628         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43629         this.view.singleSelect = false;
43630         this.view.multiSelect = true;
43631         this.view.toggleSelect = true;
43632         this.pageTb.add(new Roo.Toolbar.Fill(), {
43633             
43634             text: 'Done',
43635             handler: function()
43636             {
43637                 _t.collapse();
43638             }
43639         });
43640     },
43641     
43642     onViewOver : function(e, t){
43643         // do nothing...
43644         return;
43645         
43646     },
43647     
43648     onViewClick : function(doFocus,index){
43649         return;
43650         
43651     },
43652     select: function () {
43653         //Roo.log("SELECT CALLED");
43654     },
43655      
43656     selectByValue : function(xv, scrollIntoView){
43657         var ar = this.getValueArray();
43658         var sels = [];
43659         
43660         Roo.each(ar, function(v) {
43661             if(v === undefined || v === null){
43662                 return;
43663             }
43664             var r = this.findRecord(this.valueField, v);
43665             if(r){
43666                 sels.push(this.store.indexOf(r))
43667                 
43668             }
43669         },this);
43670         this.view.select(sels);
43671         return false;
43672     },
43673     
43674     
43675     
43676     onSelect : function(record, index){
43677        // Roo.log("onselect Called");
43678        // this is only called by the clear button now..
43679         this.view.clearSelections();
43680         this.setValue('[]');
43681         if (this.value != this.valueBefore) {
43682             this.fireEvent('change', this, this.value, this.valueBefore);
43683         }
43684     },
43685     getValueArray : function()
43686     {
43687         var ar = [] ;
43688         
43689         try {
43690             Roo.log(this.value);
43691             var ar = Roo.decode(this.value);
43692             return  ar instanceof Array ? ar : []; //?? valid?
43693             
43694         } catch(e) {
43695             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43696             return [];
43697         }
43698          
43699     },
43700     expand : function ()
43701     {
43702         Roo.form.ComboCheck.superclass.expand.call(this);
43703         this.valueBefore = this.value;
43704         
43705
43706     },
43707     
43708     collapse : function(){
43709         Roo.form.ComboCheck.superclass.collapse.call(this);
43710         var sl = this.view.getSelectedIndexes();
43711         var st = this.store;
43712         var nv = [];
43713         var tv = [];
43714         var r;
43715         Roo.each(sl, function(i) {
43716             r = st.getAt(i);
43717             nv.push(r.get(this.valueField));
43718         },this);
43719         this.setValue(Roo.encode(nv));
43720         if (this.value != this.valueBefore) {
43721
43722             this.fireEvent('change', this, this.value, this.valueBefore);
43723         }
43724         
43725     },
43726     
43727     setValue : function(v){
43728         // Roo.log(v);
43729         this.value = v;
43730         
43731         var vals = this.getValueArray();
43732         var tv = [];
43733         Roo.each(vals, function(k) {
43734             var r = this.findRecord(this.valueField, k);
43735             if(r){
43736                 tv.push(r.data[this.displayField]);
43737             }else if(this.valueNotFoundText !== undefined){
43738                 tv.push( this.valueNotFoundText );
43739             }
43740         },this);
43741        // Roo.log(tv);
43742         
43743         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43744         this.hiddenField.value = v;
43745         this.value = v;
43746     }
43747     
43748 });//<script type="text/javasscript">
43749  
43750
43751 /**
43752  * @class Roo.DDView
43753  * A DnD enabled version of Roo.View.
43754  * @param {Element/String} container The Element in which to create the View.
43755  * @param {String} tpl The template string used to create the markup for each element of the View
43756  * @param {Object} config The configuration properties. These include all the config options of
43757  * {@link Roo.View} plus some specific to this class.<br>
43758  * <p>
43759  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43760  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43761  * <p>
43762  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43763 .x-view-drag-insert-above {
43764         border-top:1px dotted #3366cc;
43765 }
43766 .x-view-drag-insert-below {
43767         border-bottom:1px dotted #3366cc;
43768 }
43769 </code></pre>
43770  * 
43771  */
43772  
43773 Roo.DDView = function(container, tpl, config) {
43774     Roo.DDView.superclass.constructor.apply(this, arguments);
43775     this.getEl().setStyle("outline", "0px none");
43776     this.getEl().unselectable();
43777     if (this.dragGroup) {
43778                 this.setDraggable(this.dragGroup.split(","));
43779     }
43780     if (this.dropGroup) {
43781                 this.setDroppable(this.dropGroup.split(","));
43782     }
43783     if (this.deletable) {
43784         this.setDeletable();
43785     }
43786     this.isDirtyFlag = false;
43787         this.addEvents({
43788                 "drop" : true
43789         });
43790 };
43791
43792 Roo.extend(Roo.DDView, Roo.View, {
43793 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43794 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43795 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43796 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43797
43798         isFormField: true,
43799
43800         reset: Roo.emptyFn,
43801         
43802         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43803
43804         validate: function() {
43805                 return true;
43806         },
43807         
43808         destroy: function() {
43809                 this.purgeListeners();
43810                 this.getEl.removeAllListeners();
43811                 this.getEl().remove();
43812                 if (this.dragZone) {
43813                         if (this.dragZone.destroy) {
43814                                 this.dragZone.destroy();
43815                         }
43816                 }
43817                 if (this.dropZone) {
43818                         if (this.dropZone.destroy) {
43819                                 this.dropZone.destroy();
43820                         }
43821                 }
43822         },
43823
43824 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43825         getName: function() {
43826                 return this.name;
43827         },
43828
43829 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43830         setValue: function(v) {
43831                 if (!this.store) {
43832                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43833                 }
43834                 var data = {};
43835                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43836                 this.store.proxy = new Roo.data.MemoryProxy(data);
43837                 this.store.load();
43838         },
43839
43840 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43841         getValue: function() {
43842                 var result = '(';
43843                 this.store.each(function(rec) {
43844                         result += rec.id + ',';
43845                 });
43846                 return result.substr(0, result.length - 1) + ')';
43847         },
43848         
43849         getIds: function() {
43850                 var i = 0, result = new Array(this.store.getCount());
43851                 this.store.each(function(rec) {
43852                         result[i++] = rec.id;
43853                 });
43854                 return result;
43855         },
43856         
43857         isDirty: function() {
43858                 return this.isDirtyFlag;
43859         },
43860
43861 /**
43862  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43863  *      whole Element becomes the target, and this causes the drop gesture to append.
43864  */
43865     getTargetFromEvent : function(e) {
43866                 var target = e.getTarget();
43867                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43868                 target = target.parentNode;
43869                 }
43870                 if (!target) {
43871                         target = this.el.dom.lastChild || this.el.dom;
43872                 }
43873                 return target;
43874     },
43875
43876 /**
43877  *      Create the drag data which consists of an object which has the property "ddel" as
43878  *      the drag proxy element. 
43879  */
43880     getDragData : function(e) {
43881         var target = this.findItemFromChild(e.getTarget());
43882                 if(target) {
43883                         this.handleSelection(e);
43884                         var selNodes = this.getSelectedNodes();
43885             var dragData = {
43886                 source: this,
43887                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43888                 nodes: selNodes,
43889                 records: []
43890                         };
43891                         var selectedIndices = this.getSelectedIndexes();
43892                         for (var i = 0; i < selectedIndices.length; i++) {
43893                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43894                         }
43895                         if (selNodes.length == 1) {
43896                                 dragData.ddel = target.cloneNode(true); // the div element
43897                         } else {
43898                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43899                                 div.className = 'multi-proxy';
43900                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43901                                         div.appendChild(selNodes[i].cloneNode(true));
43902                                 }
43903                                 dragData.ddel = div;
43904                         }
43905             //console.log(dragData)
43906             //console.log(dragData.ddel.innerHTML)
43907                         return dragData;
43908                 }
43909         //console.log('nodragData')
43910                 return false;
43911     },
43912     
43913 /**     Specify to which ddGroup items in this DDView may be dragged. */
43914     setDraggable: function(ddGroup) {
43915         if (ddGroup instanceof Array) {
43916                 Roo.each(ddGroup, this.setDraggable, this);
43917                 return;
43918         }
43919         if (this.dragZone) {
43920                 this.dragZone.addToGroup(ddGroup);
43921         } else {
43922                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43923                                 containerScroll: true,
43924                                 ddGroup: ddGroup 
43925
43926                         });
43927 //                      Draggability implies selection. DragZone's mousedown selects the element.
43928                         if (!this.multiSelect) { this.singleSelect = true; }
43929
43930 //                      Wire the DragZone's handlers up to methods in *this*
43931                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43932                 }
43933     },
43934
43935 /**     Specify from which ddGroup this DDView accepts drops. */
43936     setDroppable: function(ddGroup) {
43937         if (ddGroup instanceof Array) {
43938                 Roo.each(ddGroup, this.setDroppable, this);
43939                 return;
43940         }
43941         if (this.dropZone) {
43942                 this.dropZone.addToGroup(ddGroup);
43943         } else {
43944                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43945                                 containerScroll: true,
43946                                 ddGroup: ddGroup
43947                         });
43948
43949 //                      Wire the DropZone's handlers up to methods in *this*
43950                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43951                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43952                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43953                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43954                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43955                 }
43956     },
43957
43958 /**     Decide whether to drop above or below a View node. */
43959     getDropPoint : function(e, n, dd){
43960         if (n == this.el.dom) { return "above"; }
43961                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43962                 var c = t + (b - t) / 2;
43963                 var y = Roo.lib.Event.getPageY(e);
43964                 if(y <= c) {
43965                         return "above";
43966                 }else{
43967                         return "below";
43968                 }
43969     },
43970
43971     onNodeEnter : function(n, dd, e, data){
43972                 return false;
43973     },
43974     
43975     onNodeOver : function(n, dd, e, data){
43976                 var pt = this.getDropPoint(e, n, dd);
43977                 // set the insert point style on the target node
43978                 var dragElClass = this.dropNotAllowed;
43979                 if (pt) {
43980                         var targetElClass;
43981                         if (pt == "above"){
43982                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43983                                 targetElClass = "x-view-drag-insert-above";
43984                         } else {
43985                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43986                                 targetElClass = "x-view-drag-insert-below";
43987                         }
43988                         if (this.lastInsertClass != targetElClass){
43989                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43990                                 this.lastInsertClass = targetElClass;
43991                         }
43992                 }
43993                 return dragElClass;
43994         },
43995
43996     onNodeOut : function(n, dd, e, data){
43997                 this.removeDropIndicators(n);
43998     },
43999
44000     onNodeDrop : function(n, dd, e, data){
44001         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
44002                 return false;
44003         }
44004         var pt = this.getDropPoint(e, n, dd);
44005                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
44006                 if (pt == "below") { insertAt++; }
44007                 for (var i = 0; i < data.records.length; i++) {
44008                         var r = data.records[i];
44009                         var dup = this.store.getById(r.id);
44010                         if (dup && (dd != this.dragZone)) {
44011                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44012                         } else {
44013                                 if (data.copy) {
44014                                         this.store.insert(insertAt++, r.copy());
44015                                 } else {
44016                                         data.source.isDirtyFlag = true;
44017                                         r.store.remove(r);
44018                                         this.store.insert(insertAt++, r);
44019                                 }
44020                                 this.isDirtyFlag = true;
44021                         }
44022                 }
44023                 this.dragZone.cachedTarget = null;
44024                 return true;
44025     },
44026
44027     removeDropIndicators : function(n){
44028                 if(n){
44029                         Roo.fly(n).removeClass([
44030                                 "x-view-drag-insert-above",
44031                                 "x-view-drag-insert-below"]);
44032                         this.lastInsertClass = "_noclass";
44033                 }
44034     },
44035
44036 /**
44037  *      Utility method. Add a delete option to the DDView's context menu.
44038  *      @param {String} imageUrl The URL of the "delete" icon image.
44039  */
44040         setDeletable: function(imageUrl) {
44041                 if (!this.singleSelect && !this.multiSelect) {
44042                         this.singleSelect = true;
44043                 }
44044                 var c = this.getContextMenu();
44045                 this.contextMenu.on("itemclick", function(item) {
44046                         switch (item.id) {
44047                                 case "delete":
44048                                         this.remove(this.getSelectedIndexes());
44049                                         break;
44050                         }
44051                 }, this);
44052                 this.contextMenu.add({
44053                         icon: imageUrl,
44054                         id: "delete",
44055                         text: 'Delete'
44056                 });
44057         },
44058         
44059 /**     Return the context menu for this DDView. */
44060         getContextMenu: function() {
44061                 if (!this.contextMenu) {
44062 //                      Create the View's context menu
44063                         this.contextMenu = new Roo.menu.Menu({
44064                                 id: this.id + "-contextmenu"
44065                         });
44066                         this.el.on("contextmenu", this.showContextMenu, this);
44067                 }
44068                 return this.contextMenu;
44069         },
44070         
44071         disableContextMenu: function() {
44072                 if (this.contextMenu) {
44073                         this.el.un("contextmenu", this.showContextMenu, this);
44074                 }
44075         },
44076
44077         showContextMenu: function(e, item) {
44078         item = this.findItemFromChild(e.getTarget());
44079                 if (item) {
44080                         e.stopEvent();
44081                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44082                         this.contextMenu.showAt(e.getXY());
44083             }
44084     },
44085
44086 /**
44087  *      Remove {@link Roo.data.Record}s at the specified indices.
44088  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44089  */
44090     remove: function(selectedIndices) {
44091                 selectedIndices = [].concat(selectedIndices);
44092                 for (var i = 0; i < selectedIndices.length; i++) {
44093                         var rec = this.store.getAt(selectedIndices[i]);
44094                         this.store.remove(rec);
44095                 }
44096     },
44097
44098 /**
44099  *      Double click fires the event, but also, if this is draggable, and there is only one other
44100  *      related DropZone, it transfers the selected node.
44101  */
44102     onDblClick : function(e){
44103         var item = this.findItemFromChild(e.getTarget());
44104         if(item){
44105             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44106                 return false;
44107             }
44108             if (this.dragGroup) {
44109                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44110                     while (targets.indexOf(this.dropZone) > -1) {
44111                             targets.remove(this.dropZone);
44112                                 }
44113                     if (targets.length == 1) {
44114                                         this.dragZone.cachedTarget = null;
44115                         var el = Roo.get(targets[0].getEl());
44116                         var box = el.getBox(true);
44117                         targets[0].onNodeDrop(el.dom, {
44118                                 target: el.dom,
44119                                 xy: [box.x, box.y + box.height - 1]
44120                         }, null, this.getDragData(e));
44121                     }
44122                 }
44123         }
44124     },
44125     
44126     handleSelection: function(e) {
44127                 this.dragZone.cachedTarget = null;
44128         var item = this.findItemFromChild(e.getTarget());
44129         if (!item) {
44130                 this.clearSelections(true);
44131                 return;
44132         }
44133                 if (item && (this.multiSelect || this.singleSelect)){
44134                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44135                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44136                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44137                                 this.unselect(item);
44138                         } else {
44139                                 this.select(item, this.multiSelect && e.ctrlKey);
44140                                 this.lastSelection = item;
44141                         }
44142                 }
44143     },
44144
44145     onItemClick : function(item, index, e){
44146                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44147                         return false;
44148                 }
44149                 return true;
44150     },
44151
44152     unselect : function(nodeInfo, suppressEvent){
44153                 var node = this.getNode(nodeInfo);
44154                 if(node && this.isSelected(node)){
44155                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44156                                 Roo.fly(node).removeClass(this.selectedClass);
44157                                 this.selections.remove(node);
44158                                 if(!suppressEvent){
44159                                         this.fireEvent("selectionchange", this, this.selections);
44160                                 }
44161                         }
44162                 }
44163     }
44164 });
44165 /*
44166  * Based on:
44167  * Ext JS Library 1.1.1
44168  * Copyright(c) 2006-2007, Ext JS, LLC.
44169  *
44170  * Originally Released Under LGPL - original licence link has changed is not relivant.
44171  *
44172  * Fork - LGPL
44173  * <script type="text/javascript">
44174  */
44175  
44176 /**
44177  * @class Roo.LayoutManager
44178  * @extends Roo.util.Observable
44179  * Base class for layout managers.
44180  */
44181 Roo.LayoutManager = function(container, config){
44182     Roo.LayoutManager.superclass.constructor.call(this);
44183     this.el = Roo.get(container);
44184     // ie scrollbar fix
44185     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44186         document.body.scroll = "no";
44187     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44188         this.el.position('relative');
44189     }
44190     this.id = this.el.id;
44191     this.el.addClass("x-layout-container");
44192     /** false to disable window resize monitoring @type Boolean */
44193     this.monitorWindowResize = true;
44194     this.regions = {};
44195     this.addEvents({
44196         /**
44197          * @event layout
44198          * Fires when a layout is performed. 
44199          * @param {Roo.LayoutManager} this
44200          */
44201         "layout" : true,
44202         /**
44203          * @event regionresized
44204          * Fires when the user resizes a region. 
44205          * @param {Roo.LayoutRegion} region The resized region
44206          * @param {Number} newSize The new size (width for east/west, height for north/south)
44207          */
44208         "regionresized" : true,
44209         /**
44210          * @event regioncollapsed
44211          * Fires when a region is collapsed. 
44212          * @param {Roo.LayoutRegion} region The collapsed region
44213          */
44214         "regioncollapsed" : true,
44215         /**
44216          * @event regionexpanded
44217          * Fires when a region is expanded.  
44218          * @param {Roo.LayoutRegion} region The expanded region
44219          */
44220         "regionexpanded" : true
44221     });
44222     this.updating = false;
44223     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44224 };
44225
44226 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44227     /**
44228      * Returns true if this layout is currently being updated
44229      * @return {Boolean}
44230      */
44231     isUpdating : function(){
44232         return this.updating; 
44233     },
44234     
44235     /**
44236      * Suspend the LayoutManager from doing auto-layouts while
44237      * making multiple add or remove calls
44238      */
44239     beginUpdate : function(){
44240         this.updating = true;    
44241     },
44242     
44243     /**
44244      * Restore auto-layouts and optionally disable the manager from performing a layout
44245      * @param {Boolean} noLayout true to disable a layout update 
44246      */
44247     endUpdate : function(noLayout){
44248         this.updating = false;
44249         if(!noLayout){
44250             this.layout();
44251         }    
44252     },
44253     
44254     layout: function(){
44255         
44256     },
44257     
44258     onRegionResized : function(region, newSize){
44259         this.fireEvent("regionresized", region, newSize);
44260         this.layout();
44261     },
44262     
44263     onRegionCollapsed : function(region){
44264         this.fireEvent("regioncollapsed", region);
44265     },
44266     
44267     onRegionExpanded : function(region){
44268         this.fireEvent("regionexpanded", region);
44269     },
44270         
44271     /**
44272      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44273      * performs box-model adjustments.
44274      * @return {Object} The size as an object {width: (the width), height: (the height)}
44275      */
44276     getViewSize : function(){
44277         var size;
44278         if(this.el.dom != document.body){
44279             size = this.el.getSize();
44280         }else{
44281             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44282         }
44283         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44284         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44285         return size;
44286     },
44287     
44288     /**
44289      * Returns the Element this layout is bound to.
44290      * @return {Roo.Element}
44291      */
44292     getEl : function(){
44293         return this.el;
44294     },
44295     
44296     /**
44297      * Returns the specified region.
44298      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44299      * @return {Roo.LayoutRegion}
44300      */
44301     getRegion : function(target){
44302         return this.regions[target.toLowerCase()];
44303     },
44304     
44305     onWindowResize : function(){
44306         if(this.monitorWindowResize){
44307             this.layout();
44308         }
44309     }
44310 });/*
44311  * Based on:
44312  * Ext JS Library 1.1.1
44313  * Copyright(c) 2006-2007, Ext JS, LLC.
44314  *
44315  * Originally Released Under LGPL - original licence link has changed is not relivant.
44316  *
44317  * Fork - LGPL
44318  * <script type="text/javascript">
44319  */
44320 /**
44321  * @class Roo.BorderLayout
44322  * @extends Roo.LayoutManager
44323  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44324  * please see: <br><br>
44325  * <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>
44326  * <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>
44327  * Example:
44328  <pre><code>
44329  var layout = new Roo.BorderLayout(document.body, {
44330     north: {
44331         initialSize: 25,
44332         titlebar: false
44333     },
44334     west: {
44335         split:true,
44336         initialSize: 200,
44337         minSize: 175,
44338         maxSize: 400,
44339         titlebar: true,
44340         collapsible: true
44341     },
44342     east: {
44343         split:true,
44344         initialSize: 202,
44345         minSize: 175,
44346         maxSize: 400,
44347         titlebar: true,
44348         collapsible: true
44349     },
44350     south: {
44351         split:true,
44352         initialSize: 100,
44353         minSize: 100,
44354         maxSize: 200,
44355         titlebar: true,
44356         collapsible: true
44357     },
44358     center: {
44359         titlebar: true,
44360         autoScroll:true,
44361         resizeTabs: true,
44362         minTabWidth: 50,
44363         preferredTabWidth: 150
44364     }
44365 });
44366
44367 // shorthand
44368 var CP = Roo.ContentPanel;
44369
44370 layout.beginUpdate();
44371 layout.add("north", new CP("north", "North"));
44372 layout.add("south", new CP("south", {title: "South", closable: true}));
44373 layout.add("west", new CP("west", {title: "West"}));
44374 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44375 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44376 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44377 layout.getRegion("center").showPanel("center1");
44378 layout.endUpdate();
44379 </code></pre>
44380
44381 <b>The container the layout is rendered into can be either the body element or any other element.
44382 If it is not the body element, the container needs to either be an absolute positioned element,
44383 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44384 the container size if it is not the body element.</b>
44385
44386 * @constructor
44387 * Create a new BorderLayout
44388 * @param {String/HTMLElement/Element} container The container this layout is bound to
44389 * @param {Object} config Configuration options
44390  */
44391 Roo.BorderLayout = function(container, config){
44392     config = config || {};
44393     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44394     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44395     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44396         var target = this.factory.validRegions[i];
44397         if(config[target]){
44398             this.addRegion(target, config[target]);
44399         }
44400     }
44401 };
44402
44403 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44404     /**
44405      * Creates and adds a new region if it doesn't already exist.
44406      * @param {String} target The target region key (north, south, east, west or center).
44407      * @param {Object} config The regions config object
44408      * @return {BorderLayoutRegion} The new region
44409      */
44410     addRegion : function(target, config){
44411         if(!this.regions[target]){
44412             var r = this.factory.create(target, this, config);
44413             this.bindRegion(target, r);
44414         }
44415         return this.regions[target];
44416     },
44417
44418     // private (kinda)
44419     bindRegion : function(name, r){
44420         this.regions[name] = r;
44421         r.on("visibilitychange", this.layout, this);
44422         r.on("paneladded", this.layout, this);
44423         r.on("panelremoved", this.layout, this);
44424         r.on("invalidated", this.layout, this);
44425         r.on("resized", this.onRegionResized, this);
44426         r.on("collapsed", this.onRegionCollapsed, this);
44427         r.on("expanded", this.onRegionExpanded, this);
44428     },
44429
44430     /**
44431      * Performs a layout update.
44432      */
44433     layout : function(){
44434         if(this.updating) return;
44435         var size = this.getViewSize();
44436         var w = size.width;
44437         var h = size.height;
44438         var centerW = w;
44439         var centerH = h;
44440         var centerY = 0;
44441         var centerX = 0;
44442         //var x = 0, y = 0;
44443
44444         var rs = this.regions;
44445         var north = rs["north"];
44446         var south = rs["south"]; 
44447         var west = rs["west"];
44448         var east = rs["east"];
44449         var center = rs["center"];
44450         //if(this.hideOnLayout){ // not supported anymore
44451             //c.el.setStyle("display", "none");
44452         //}
44453         if(north && north.isVisible()){
44454             var b = north.getBox();
44455             var m = north.getMargins();
44456             b.width = w - (m.left+m.right);
44457             b.x = m.left;
44458             b.y = m.top;
44459             centerY = b.height + b.y + m.bottom;
44460             centerH -= centerY;
44461             north.updateBox(this.safeBox(b));
44462         }
44463         if(south && south.isVisible()){
44464             var b = south.getBox();
44465             var m = south.getMargins();
44466             b.width = w - (m.left+m.right);
44467             b.x = m.left;
44468             var totalHeight = (b.height + m.top + m.bottom);
44469             b.y = h - totalHeight + m.top;
44470             centerH -= totalHeight;
44471             south.updateBox(this.safeBox(b));
44472         }
44473         if(west && west.isVisible()){
44474             var b = west.getBox();
44475             var m = west.getMargins();
44476             b.height = centerH - (m.top+m.bottom);
44477             b.x = m.left;
44478             b.y = centerY + m.top;
44479             var totalWidth = (b.width + m.left + m.right);
44480             centerX += totalWidth;
44481             centerW -= totalWidth;
44482             west.updateBox(this.safeBox(b));
44483         }
44484         if(east && east.isVisible()){
44485             var b = east.getBox();
44486             var m = east.getMargins();
44487             b.height = centerH - (m.top+m.bottom);
44488             var totalWidth = (b.width + m.left + m.right);
44489             b.x = w - totalWidth + m.left;
44490             b.y = centerY + m.top;
44491             centerW -= totalWidth;
44492             east.updateBox(this.safeBox(b));
44493         }
44494         if(center){
44495             var m = center.getMargins();
44496             var centerBox = {
44497                 x: centerX + m.left,
44498                 y: centerY + m.top,
44499                 width: centerW - (m.left+m.right),
44500                 height: centerH - (m.top+m.bottom)
44501             };
44502             //if(this.hideOnLayout){
44503                 //center.el.setStyle("display", "block");
44504             //}
44505             center.updateBox(this.safeBox(centerBox));
44506         }
44507         this.el.repaint();
44508         this.fireEvent("layout", this);
44509     },
44510
44511     // private
44512     safeBox : function(box){
44513         box.width = Math.max(0, box.width);
44514         box.height = Math.max(0, box.height);
44515         return box;
44516     },
44517
44518     /**
44519      * Adds a ContentPanel (or subclass) to this layout.
44520      * @param {String} target The target region key (north, south, east, west or center).
44521      * @param {Roo.ContentPanel} panel The panel to add
44522      * @return {Roo.ContentPanel} The added panel
44523      */
44524     add : function(target, panel){
44525          
44526         target = target.toLowerCase();
44527         return this.regions[target].add(panel);
44528     },
44529
44530     /**
44531      * Remove a ContentPanel (or subclass) to this layout.
44532      * @param {String} target The target region key (north, south, east, west or center).
44533      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44534      * @return {Roo.ContentPanel} The removed panel
44535      */
44536     remove : function(target, panel){
44537         target = target.toLowerCase();
44538         return this.regions[target].remove(panel);
44539     },
44540
44541     /**
44542      * Searches all regions for a panel with the specified id
44543      * @param {String} panelId
44544      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44545      */
44546     findPanel : function(panelId){
44547         var rs = this.regions;
44548         for(var target in rs){
44549             if(typeof rs[target] != "function"){
44550                 var p = rs[target].getPanel(panelId);
44551                 if(p){
44552                     return p;
44553                 }
44554             }
44555         }
44556         return null;
44557     },
44558
44559     /**
44560      * Searches all regions for a panel with the specified id and activates (shows) it.
44561      * @param {String/ContentPanel} panelId The panels id or the panel itself
44562      * @return {Roo.ContentPanel} The shown panel or null
44563      */
44564     showPanel : function(panelId) {
44565       var rs = this.regions;
44566       for(var target in rs){
44567          var r = rs[target];
44568          if(typeof r != "function"){
44569             if(r.hasPanel(panelId)){
44570                return r.showPanel(panelId);
44571             }
44572          }
44573       }
44574       return null;
44575    },
44576
44577    /**
44578      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44579      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44580      */
44581     restoreState : function(provider){
44582         if(!provider){
44583             provider = Roo.state.Manager;
44584         }
44585         var sm = new Roo.LayoutStateManager();
44586         sm.init(this, provider);
44587     },
44588
44589     /**
44590      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44591      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44592      * a valid ContentPanel config object.  Example:
44593      * <pre><code>
44594 // Create the main layout
44595 var layout = new Roo.BorderLayout('main-ct', {
44596     west: {
44597         split:true,
44598         minSize: 175,
44599         titlebar: true
44600     },
44601     center: {
44602         title:'Components'
44603     }
44604 }, 'main-ct');
44605
44606 // Create and add multiple ContentPanels at once via configs
44607 layout.batchAdd({
44608    west: {
44609        id: 'source-files',
44610        autoCreate:true,
44611        title:'Ext Source Files',
44612        autoScroll:true,
44613        fitToFrame:true
44614    },
44615    center : {
44616        el: cview,
44617        autoScroll:true,
44618        fitToFrame:true,
44619        toolbar: tb,
44620        resizeEl:'cbody'
44621    }
44622 });
44623 </code></pre>
44624      * @param {Object} regions An object containing ContentPanel configs by region name
44625      */
44626     batchAdd : function(regions){
44627         this.beginUpdate();
44628         for(var rname in regions){
44629             var lr = this.regions[rname];
44630             if(lr){
44631                 this.addTypedPanels(lr, regions[rname]);
44632             }
44633         }
44634         this.endUpdate();
44635     },
44636
44637     // private
44638     addTypedPanels : function(lr, ps){
44639         if(typeof ps == 'string'){
44640             lr.add(new Roo.ContentPanel(ps));
44641         }
44642         else if(ps instanceof Array){
44643             for(var i =0, len = ps.length; i < len; i++){
44644                 this.addTypedPanels(lr, ps[i]);
44645             }
44646         }
44647         else if(!ps.events){ // raw config?
44648             var el = ps.el;
44649             delete ps.el; // prevent conflict
44650             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44651         }
44652         else {  // panel object assumed!
44653             lr.add(ps);
44654         }
44655     },
44656     /**
44657      * Adds a xtype elements to the layout.
44658      * <pre><code>
44659
44660 layout.addxtype({
44661        xtype : 'ContentPanel',
44662        region: 'west',
44663        items: [ .... ]
44664    }
44665 );
44666
44667 layout.addxtype({
44668         xtype : 'NestedLayoutPanel',
44669         region: 'west',
44670         layout: {
44671            center: { },
44672            west: { }   
44673         },
44674         items : [ ... list of content panels or nested layout panels.. ]
44675    }
44676 );
44677 </code></pre>
44678      * @param {Object} cfg Xtype definition of item to add.
44679      */
44680     addxtype : function(cfg)
44681     {
44682         // basically accepts a pannel...
44683         // can accept a layout region..!?!?
44684         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44685         
44686         if (!cfg.xtype.match(/Panel$/)) {
44687             return false;
44688         }
44689         var ret = false;
44690         
44691         if (typeof(cfg.region) == 'undefined') {
44692             Roo.log("Failed to add Panel, region was not set");
44693             Roo.log(cfg);
44694             return false;
44695         }
44696         var region = cfg.region;
44697         delete cfg.region;
44698         
44699           
44700         var xitems = [];
44701         if (cfg.items) {
44702             xitems = cfg.items;
44703             delete cfg.items;
44704         }
44705         
44706         
44707         switch(cfg.xtype) 
44708         {
44709             case 'ContentPanel':  // ContentPanel (el, cfg)
44710             case 'ScrollPanel':  // ContentPanel (el, cfg)
44711                 if(cfg.autoCreate) {
44712                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44713                 } else {
44714                     var el = this.el.createChild();
44715                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44716                 }
44717                 
44718                 this.add(region, ret);
44719                 break;
44720             
44721             
44722             case 'TreePanel': // our new panel!
44723                 cfg.el = this.el.createChild();
44724                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44725                 this.add(region, ret);
44726                 break;
44727             
44728             case 'NestedLayoutPanel': 
44729                 // create a new Layout (which is  a Border Layout...
44730                 var el = this.el.createChild();
44731                 var clayout = cfg.layout;
44732                 delete cfg.layout;
44733                 clayout.items   = clayout.items  || [];
44734                 // replace this exitems with the clayout ones..
44735                 xitems = clayout.items;
44736                  
44737                 
44738                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44739                     cfg.background = false;
44740                 }
44741                 var layout = new Roo.BorderLayout(el, clayout);
44742                 
44743                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44744                 //console.log('adding nested layout panel '  + cfg.toSource());
44745                 this.add(region, ret);
44746                 
44747                 break;
44748                 
44749             case 'GridPanel': 
44750             
44751                 // needs grid and region
44752                 
44753                 //var el = this.getRegion(region).el.createChild();
44754                 var el = this.el.createChild();
44755                 // create the grid first...
44756                 
44757                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44758                 delete cfg.grid;
44759                 if (region == 'center' && this.active ) {
44760                     cfg.background = false;
44761                 }
44762                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44763                 
44764                 this.add(region, ret);
44765                 if (cfg.background) {
44766                     ret.on('activate', function(gp) {
44767                         if (!gp.grid.rendered) {
44768                             gp.grid.render();
44769                         }
44770                     });
44771                 } else {
44772                     grid.render();
44773                 }
44774                 break;
44775            
44776                
44777                 
44778                 
44779             default: 
44780                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44781                 return null;
44782              // GridPanel (grid, cfg)
44783             
44784         }
44785         this.beginUpdate();
44786         // add children..
44787         Roo.each(xitems, function(i)  {
44788             ret.addxtype(i);
44789         });
44790         this.endUpdate();
44791         return ret;
44792         
44793     }
44794 });
44795
44796 /**
44797  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44798  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44799  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44800  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44801  * <pre><code>
44802 // shorthand
44803 var CP = Roo.ContentPanel;
44804
44805 var layout = Roo.BorderLayout.create({
44806     north: {
44807         initialSize: 25,
44808         titlebar: false,
44809         panels: [new CP("north", "North")]
44810     },
44811     west: {
44812         split:true,
44813         initialSize: 200,
44814         minSize: 175,
44815         maxSize: 400,
44816         titlebar: true,
44817         collapsible: true,
44818         panels: [new CP("west", {title: "West"})]
44819     },
44820     east: {
44821         split:true,
44822         initialSize: 202,
44823         minSize: 175,
44824         maxSize: 400,
44825         titlebar: true,
44826         collapsible: true,
44827         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44828     },
44829     south: {
44830         split:true,
44831         initialSize: 100,
44832         minSize: 100,
44833         maxSize: 200,
44834         titlebar: true,
44835         collapsible: true,
44836         panels: [new CP("south", {title: "South", closable: true})]
44837     },
44838     center: {
44839         titlebar: true,
44840         autoScroll:true,
44841         resizeTabs: true,
44842         minTabWidth: 50,
44843         preferredTabWidth: 150,
44844         panels: [
44845             new CP("center1", {title: "Close Me", closable: true}),
44846             new CP("center2", {title: "Center Panel", closable: false})
44847         ]
44848     }
44849 }, document.body);
44850
44851 layout.getRegion("center").showPanel("center1");
44852 </code></pre>
44853  * @param config
44854  * @param targetEl
44855  */
44856 Roo.BorderLayout.create = function(config, targetEl){
44857     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44858     layout.beginUpdate();
44859     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44860     for(var j = 0, jlen = regions.length; j < jlen; j++){
44861         var lr = regions[j];
44862         if(layout.regions[lr] && config[lr].panels){
44863             var r = layout.regions[lr];
44864             var ps = config[lr].panels;
44865             layout.addTypedPanels(r, ps);
44866         }
44867     }
44868     layout.endUpdate();
44869     return layout;
44870 };
44871
44872 // private
44873 Roo.BorderLayout.RegionFactory = {
44874     // private
44875     validRegions : ["north","south","east","west","center"],
44876
44877     // private
44878     create : function(target, mgr, config){
44879         target = target.toLowerCase();
44880         if(config.lightweight || config.basic){
44881             return new Roo.BasicLayoutRegion(mgr, config, target);
44882         }
44883         switch(target){
44884             case "north":
44885                 return new Roo.NorthLayoutRegion(mgr, config);
44886             case "south":
44887                 return new Roo.SouthLayoutRegion(mgr, config);
44888             case "east":
44889                 return new Roo.EastLayoutRegion(mgr, config);
44890             case "west":
44891                 return new Roo.WestLayoutRegion(mgr, config);
44892             case "center":
44893                 return new Roo.CenterLayoutRegion(mgr, config);
44894         }
44895         throw 'Layout region "'+target+'" not supported.';
44896     }
44897 };/*
44898  * Based on:
44899  * Ext JS Library 1.1.1
44900  * Copyright(c) 2006-2007, Ext JS, LLC.
44901  *
44902  * Originally Released Under LGPL - original licence link has changed is not relivant.
44903  *
44904  * Fork - LGPL
44905  * <script type="text/javascript">
44906  */
44907  
44908 /**
44909  * @class Roo.BasicLayoutRegion
44910  * @extends Roo.util.Observable
44911  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44912  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44913  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44914  */
44915 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44916     this.mgr = mgr;
44917     this.position  = pos;
44918     this.events = {
44919         /**
44920          * @scope Roo.BasicLayoutRegion
44921          */
44922         
44923         /**
44924          * @event beforeremove
44925          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44926          * @param {Roo.LayoutRegion} this
44927          * @param {Roo.ContentPanel} panel The panel
44928          * @param {Object} e The cancel event object
44929          */
44930         "beforeremove" : true,
44931         /**
44932          * @event invalidated
44933          * Fires when the layout for this region is changed.
44934          * @param {Roo.LayoutRegion} this
44935          */
44936         "invalidated" : true,
44937         /**
44938          * @event visibilitychange
44939          * Fires when this region is shown or hidden 
44940          * @param {Roo.LayoutRegion} this
44941          * @param {Boolean} visibility true or false
44942          */
44943         "visibilitychange" : true,
44944         /**
44945          * @event paneladded
44946          * Fires when a panel is added. 
44947          * @param {Roo.LayoutRegion} this
44948          * @param {Roo.ContentPanel} panel The panel
44949          */
44950         "paneladded" : true,
44951         /**
44952          * @event panelremoved
44953          * Fires when a panel is removed. 
44954          * @param {Roo.LayoutRegion} this
44955          * @param {Roo.ContentPanel} panel The panel
44956          */
44957         "panelremoved" : true,
44958         /**
44959          * @event collapsed
44960          * Fires when this region is collapsed.
44961          * @param {Roo.LayoutRegion} this
44962          */
44963         "collapsed" : true,
44964         /**
44965          * @event expanded
44966          * Fires when this region is expanded.
44967          * @param {Roo.LayoutRegion} this
44968          */
44969         "expanded" : true,
44970         /**
44971          * @event slideshow
44972          * Fires when this region is slid into view.
44973          * @param {Roo.LayoutRegion} this
44974          */
44975         "slideshow" : true,
44976         /**
44977          * @event slidehide
44978          * Fires when this region slides out of view. 
44979          * @param {Roo.LayoutRegion} this
44980          */
44981         "slidehide" : true,
44982         /**
44983          * @event panelactivated
44984          * Fires when a panel is activated. 
44985          * @param {Roo.LayoutRegion} this
44986          * @param {Roo.ContentPanel} panel The activated panel
44987          */
44988         "panelactivated" : true,
44989         /**
44990          * @event resized
44991          * Fires when the user resizes this region. 
44992          * @param {Roo.LayoutRegion} this
44993          * @param {Number} newSize The new size (width for east/west, height for north/south)
44994          */
44995         "resized" : true
44996     };
44997     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44998     this.panels = new Roo.util.MixedCollection();
44999     this.panels.getKey = this.getPanelId.createDelegate(this);
45000     this.box = null;
45001     this.activePanel = null;
45002     // ensure listeners are added...
45003     
45004     if (config.listeners || config.events) {
45005         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
45006             listeners : config.listeners || {},
45007             events : config.events || {}
45008         });
45009     }
45010     
45011     if(skipConfig !== true){
45012         this.applyConfig(config);
45013     }
45014 };
45015
45016 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45017     getPanelId : function(p){
45018         return p.getId();
45019     },
45020     
45021     applyConfig : function(config){
45022         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45023         this.config = config;
45024         
45025     },
45026     
45027     /**
45028      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45029      * the width, for horizontal (north, south) the height.
45030      * @param {Number} newSize The new width or height
45031      */
45032     resizeTo : function(newSize){
45033         var el = this.el ? this.el :
45034                  (this.activePanel ? this.activePanel.getEl() : null);
45035         if(el){
45036             switch(this.position){
45037                 case "east":
45038                 case "west":
45039                     el.setWidth(newSize);
45040                     this.fireEvent("resized", this, newSize);
45041                 break;
45042                 case "north":
45043                 case "south":
45044                     el.setHeight(newSize);
45045                     this.fireEvent("resized", this, newSize);
45046                 break;                
45047             }
45048         }
45049     },
45050     
45051     getBox : function(){
45052         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45053     },
45054     
45055     getMargins : function(){
45056         return this.margins;
45057     },
45058     
45059     updateBox : function(box){
45060         this.box = box;
45061         var el = this.activePanel.getEl();
45062         el.dom.style.left = box.x + "px";
45063         el.dom.style.top = box.y + "px";
45064         this.activePanel.setSize(box.width, box.height);
45065     },
45066     
45067     /**
45068      * Returns the container element for this region.
45069      * @return {Roo.Element}
45070      */
45071     getEl : function(){
45072         return this.activePanel;
45073     },
45074     
45075     /**
45076      * Returns true if this region is currently visible.
45077      * @return {Boolean}
45078      */
45079     isVisible : function(){
45080         return this.activePanel ? true : false;
45081     },
45082     
45083     setActivePanel : function(panel){
45084         panel = this.getPanel(panel);
45085         if(this.activePanel && this.activePanel != panel){
45086             this.activePanel.setActiveState(false);
45087             this.activePanel.getEl().setLeftTop(-10000,-10000);
45088         }
45089         this.activePanel = panel;
45090         panel.setActiveState(true);
45091         if(this.box){
45092             panel.setSize(this.box.width, this.box.height);
45093         }
45094         this.fireEvent("panelactivated", this, panel);
45095         this.fireEvent("invalidated");
45096     },
45097     
45098     /**
45099      * Show the specified panel.
45100      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45101      * @return {Roo.ContentPanel} The shown panel or null
45102      */
45103     showPanel : function(panel){
45104         if(panel = this.getPanel(panel)){
45105             this.setActivePanel(panel);
45106         }
45107         return panel;
45108     },
45109     
45110     /**
45111      * Get the active panel for this region.
45112      * @return {Roo.ContentPanel} The active panel or null
45113      */
45114     getActivePanel : function(){
45115         return this.activePanel;
45116     },
45117     
45118     /**
45119      * Add the passed ContentPanel(s)
45120      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45121      * @return {Roo.ContentPanel} The panel added (if only one was added)
45122      */
45123     add : function(panel){
45124         if(arguments.length > 1){
45125             for(var i = 0, len = arguments.length; i < len; i++) {
45126                 this.add(arguments[i]);
45127             }
45128             return null;
45129         }
45130         if(this.hasPanel(panel)){
45131             this.showPanel(panel);
45132             return panel;
45133         }
45134         var el = panel.getEl();
45135         if(el.dom.parentNode != this.mgr.el.dom){
45136             this.mgr.el.dom.appendChild(el.dom);
45137         }
45138         if(panel.setRegion){
45139             panel.setRegion(this);
45140         }
45141         this.panels.add(panel);
45142         el.setStyle("position", "absolute");
45143         if(!panel.background){
45144             this.setActivePanel(panel);
45145             if(this.config.initialSize && this.panels.getCount()==1){
45146                 this.resizeTo(this.config.initialSize);
45147             }
45148         }
45149         this.fireEvent("paneladded", this, panel);
45150         return panel;
45151     },
45152     
45153     /**
45154      * Returns true if the panel is in this region.
45155      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45156      * @return {Boolean}
45157      */
45158     hasPanel : function(panel){
45159         if(typeof panel == "object"){ // must be panel obj
45160             panel = panel.getId();
45161         }
45162         return this.getPanel(panel) ? true : false;
45163     },
45164     
45165     /**
45166      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45167      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45168      * @param {Boolean} preservePanel Overrides the config preservePanel option
45169      * @return {Roo.ContentPanel} The panel that was removed
45170      */
45171     remove : function(panel, preservePanel){
45172         panel = this.getPanel(panel);
45173         if(!panel){
45174             return null;
45175         }
45176         var e = {};
45177         this.fireEvent("beforeremove", this, panel, e);
45178         if(e.cancel === true){
45179             return null;
45180         }
45181         var panelId = panel.getId();
45182         this.panels.removeKey(panelId);
45183         return panel;
45184     },
45185     
45186     /**
45187      * Returns the panel specified or null if it's not in this region.
45188      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45189      * @return {Roo.ContentPanel}
45190      */
45191     getPanel : function(id){
45192         if(typeof id == "object"){ // must be panel obj
45193             return id;
45194         }
45195         return this.panels.get(id);
45196     },
45197     
45198     /**
45199      * Returns this regions position (north/south/east/west/center).
45200      * @return {String} 
45201      */
45202     getPosition: function(){
45203         return this.position;    
45204     }
45205 });/*
45206  * Based on:
45207  * Ext JS Library 1.1.1
45208  * Copyright(c) 2006-2007, Ext JS, LLC.
45209  *
45210  * Originally Released Under LGPL - original licence link has changed is not relivant.
45211  *
45212  * Fork - LGPL
45213  * <script type="text/javascript">
45214  */
45215  
45216 /**
45217  * @class Roo.LayoutRegion
45218  * @extends Roo.BasicLayoutRegion
45219  * This class represents a region in a layout manager.
45220  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45221  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45222  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45223  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45224  * @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})
45225  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45226  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45227  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45228  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45229  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45230  * @cfg {String}    title           The title for the region (overrides panel titles)
45231  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45232  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45233  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45234  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45235  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45236  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45237  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45238  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45239  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45240  * @cfg {Boolean}   showPin         True to show a pin button
45241  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45242  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45243  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45244  * @cfg {Number}    width           For East/West panels
45245  * @cfg {Number}    height          For North/South panels
45246  * @cfg {Boolean}   split           To show the splitter
45247  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45248  */
45249 Roo.LayoutRegion = function(mgr, config, pos){
45250     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45251     var dh = Roo.DomHelper;
45252     /** This region's container element 
45253     * @type Roo.Element */
45254     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45255     /** This region's title element 
45256     * @type Roo.Element */
45257
45258     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45259         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45260         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45261     ]}, true);
45262     this.titleEl.enableDisplayMode();
45263     /** This region's title text element 
45264     * @type HTMLElement */
45265     this.titleTextEl = this.titleEl.dom.firstChild;
45266     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45267     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45268     this.closeBtn.enableDisplayMode();
45269     this.closeBtn.on("click", this.closeClicked, this);
45270     this.closeBtn.hide();
45271
45272     this.createBody(config);
45273     this.visible = true;
45274     this.collapsed = false;
45275
45276     if(config.hideWhenEmpty){
45277         this.hide();
45278         this.on("paneladded", this.validateVisibility, this);
45279         this.on("panelremoved", this.validateVisibility, this);
45280     }
45281     this.applyConfig(config);
45282 };
45283
45284 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45285
45286     createBody : function(){
45287         /** This region's body element 
45288         * @type Roo.Element */
45289         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45290     },
45291
45292     applyConfig : function(c){
45293         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45294             var dh = Roo.DomHelper;
45295             if(c.titlebar !== false){
45296                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45297                 this.collapseBtn.on("click", this.collapse, this);
45298                 this.collapseBtn.enableDisplayMode();
45299
45300                 if(c.showPin === true || this.showPin){
45301                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45302                     this.stickBtn.enableDisplayMode();
45303                     this.stickBtn.on("click", this.expand, this);
45304                     this.stickBtn.hide();
45305                 }
45306             }
45307             /** This region's collapsed element
45308             * @type Roo.Element */
45309             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45310                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45311             ]}, true);
45312             if(c.floatable !== false){
45313                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45314                this.collapsedEl.on("click", this.collapseClick, this);
45315             }
45316
45317             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45318                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45319                    id: "message", unselectable: "on", style:{"float":"left"}});
45320                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45321              }
45322             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45323             this.expandBtn.on("click", this.expand, this);
45324         }
45325         if(this.collapseBtn){
45326             this.collapseBtn.setVisible(c.collapsible == true);
45327         }
45328         this.cmargins = c.cmargins || this.cmargins ||
45329                          (this.position == "west" || this.position == "east" ?
45330                              {top: 0, left: 2, right:2, bottom: 0} :
45331                              {top: 2, left: 0, right:0, bottom: 2});
45332         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45333         this.bottomTabs = c.tabPosition != "top";
45334         this.autoScroll = c.autoScroll || false;
45335         if(this.autoScroll){
45336             this.bodyEl.setStyle("overflow", "auto");
45337         }else{
45338             this.bodyEl.setStyle("overflow", "hidden");
45339         }
45340         //if(c.titlebar !== false){
45341             if((!c.titlebar && !c.title) || c.titlebar === false){
45342                 this.titleEl.hide();
45343             }else{
45344                 this.titleEl.show();
45345                 if(c.title){
45346                     this.titleTextEl.innerHTML = c.title;
45347                 }
45348             }
45349         //}
45350         this.duration = c.duration || .30;
45351         this.slideDuration = c.slideDuration || .45;
45352         this.config = c;
45353         if(c.collapsed){
45354             this.collapse(true);
45355         }
45356         if(c.hidden){
45357             this.hide();
45358         }
45359     },
45360     /**
45361      * Returns true if this region is currently visible.
45362      * @return {Boolean}
45363      */
45364     isVisible : function(){
45365         return this.visible;
45366     },
45367
45368     /**
45369      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45370      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45371      */
45372     setCollapsedTitle : function(title){
45373         title = title || "&#160;";
45374         if(this.collapsedTitleTextEl){
45375             this.collapsedTitleTextEl.innerHTML = title;
45376         }
45377     },
45378
45379     getBox : function(){
45380         var b;
45381         if(!this.collapsed){
45382             b = this.el.getBox(false, true);
45383         }else{
45384             b = this.collapsedEl.getBox(false, true);
45385         }
45386         return b;
45387     },
45388
45389     getMargins : function(){
45390         return this.collapsed ? this.cmargins : this.margins;
45391     },
45392
45393     highlight : function(){
45394         this.el.addClass("x-layout-panel-dragover");
45395     },
45396
45397     unhighlight : function(){
45398         this.el.removeClass("x-layout-panel-dragover");
45399     },
45400
45401     updateBox : function(box){
45402         this.box = box;
45403         if(!this.collapsed){
45404             this.el.dom.style.left = box.x + "px";
45405             this.el.dom.style.top = box.y + "px";
45406             this.updateBody(box.width, box.height);
45407         }else{
45408             this.collapsedEl.dom.style.left = box.x + "px";
45409             this.collapsedEl.dom.style.top = box.y + "px";
45410             this.collapsedEl.setSize(box.width, box.height);
45411         }
45412         if(this.tabs){
45413             this.tabs.autoSizeTabs();
45414         }
45415     },
45416
45417     updateBody : function(w, h){
45418         if(w !== null){
45419             this.el.setWidth(w);
45420             w -= this.el.getBorderWidth("rl");
45421             if(this.config.adjustments){
45422                 w += this.config.adjustments[0];
45423             }
45424         }
45425         if(h !== null){
45426             this.el.setHeight(h);
45427             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45428             h -= this.el.getBorderWidth("tb");
45429             if(this.config.adjustments){
45430                 h += this.config.adjustments[1];
45431             }
45432             this.bodyEl.setHeight(h);
45433             if(this.tabs){
45434                 h = this.tabs.syncHeight(h);
45435             }
45436         }
45437         if(this.panelSize){
45438             w = w !== null ? w : this.panelSize.width;
45439             h = h !== null ? h : this.panelSize.height;
45440         }
45441         if(this.activePanel){
45442             var el = this.activePanel.getEl();
45443             w = w !== null ? w : el.getWidth();
45444             h = h !== null ? h : el.getHeight();
45445             this.panelSize = {width: w, height: h};
45446             this.activePanel.setSize(w, h);
45447         }
45448         if(Roo.isIE && this.tabs){
45449             this.tabs.el.repaint();
45450         }
45451     },
45452
45453     /**
45454      * Returns the container element for this region.
45455      * @return {Roo.Element}
45456      */
45457     getEl : function(){
45458         return this.el;
45459     },
45460
45461     /**
45462      * Hides this region.
45463      */
45464     hide : function(){
45465         if(!this.collapsed){
45466             this.el.dom.style.left = "-2000px";
45467             this.el.hide();
45468         }else{
45469             this.collapsedEl.dom.style.left = "-2000px";
45470             this.collapsedEl.hide();
45471         }
45472         this.visible = false;
45473         this.fireEvent("visibilitychange", this, false);
45474     },
45475
45476     /**
45477      * Shows this region if it was previously hidden.
45478      */
45479     show : function(){
45480         if(!this.collapsed){
45481             this.el.show();
45482         }else{
45483             this.collapsedEl.show();
45484         }
45485         this.visible = true;
45486         this.fireEvent("visibilitychange", this, true);
45487     },
45488
45489     closeClicked : function(){
45490         if(this.activePanel){
45491             this.remove(this.activePanel);
45492         }
45493     },
45494
45495     collapseClick : function(e){
45496         if(this.isSlid){
45497            e.stopPropagation();
45498            this.slideIn();
45499         }else{
45500            e.stopPropagation();
45501            this.slideOut();
45502         }
45503     },
45504
45505     /**
45506      * Collapses this region.
45507      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45508      */
45509     collapse : function(skipAnim){
45510         if(this.collapsed) return;
45511         this.collapsed = true;
45512         if(this.split){
45513             this.split.el.hide();
45514         }
45515         if(this.config.animate && skipAnim !== true){
45516             this.fireEvent("invalidated", this);
45517             this.animateCollapse();
45518         }else{
45519             this.el.setLocation(-20000,-20000);
45520             this.el.hide();
45521             this.collapsedEl.show();
45522             this.fireEvent("collapsed", this);
45523             this.fireEvent("invalidated", this);
45524         }
45525     },
45526
45527     animateCollapse : function(){
45528         // overridden
45529     },
45530
45531     /**
45532      * Expands this region if it was previously collapsed.
45533      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45534      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45535      */
45536     expand : function(e, skipAnim){
45537         if(e) e.stopPropagation();
45538         if(!this.collapsed || this.el.hasActiveFx()) return;
45539         if(this.isSlid){
45540             this.afterSlideIn();
45541             skipAnim = true;
45542         }
45543         this.collapsed = false;
45544         if(this.config.animate && skipAnim !== true){
45545             this.animateExpand();
45546         }else{
45547             this.el.show();
45548             if(this.split){
45549                 this.split.el.show();
45550             }
45551             this.collapsedEl.setLocation(-2000,-2000);
45552             this.collapsedEl.hide();
45553             this.fireEvent("invalidated", this);
45554             this.fireEvent("expanded", this);
45555         }
45556     },
45557
45558     animateExpand : function(){
45559         // overridden
45560     },
45561
45562     initTabs : function()
45563     {
45564         this.bodyEl.setStyle("overflow", "hidden");
45565         var ts = new Roo.TabPanel(
45566                 this.bodyEl.dom,
45567                 {
45568                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45569                     disableTooltips: this.config.disableTabTips,
45570                     toolbar : this.config.toolbar
45571                 }
45572         );
45573         if(this.config.hideTabs){
45574             ts.stripWrap.setDisplayed(false);
45575         }
45576         this.tabs = ts;
45577         ts.resizeTabs = this.config.resizeTabs === true;
45578         ts.minTabWidth = this.config.minTabWidth || 40;
45579         ts.maxTabWidth = this.config.maxTabWidth || 250;
45580         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45581         ts.monitorResize = false;
45582         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45583         ts.bodyEl.addClass('x-layout-tabs-body');
45584         this.panels.each(this.initPanelAsTab, this);
45585     },
45586
45587     initPanelAsTab : function(panel){
45588         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45589                     this.config.closeOnTab && panel.isClosable());
45590         if(panel.tabTip !== undefined){
45591             ti.setTooltip(panel.tabTip);
45592         }
45593         ti.on("activate", function(){
45594               this.setActivePanel(panel);
45595         }, this);
45596         if(this.config.closeOnTab){
45597             ti.on("beforeclose", function(t, e){
45598                 e.cancel = true;
45599                 this.remove(panel);
45600             }, this);
45601         }
45602         return ti;
45603     },
45604
45605     updatePanelTitle : function(panel, title){
45606         if(this.activePanel == panel){
45607             this.updateTitle(title);
45608         }
45609         if(this.tabs){
45610             var ti = this.tabs.getTab(panel.getEl().id);
45611             ti.setText(title);
45612             if(panel.tabTip !== undefined){
45613                 ti.setTooltip(panel.tabTip);
45614             }
45615         }
45616     },
45617
45618     updateTitle : function(title){
45619         if(this.titleTextEl && !this.config.title){
45620             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45621         }
45622     },
45623
45624     setActivePanel : function(panel){
45625         panel = this.getPanel(panel);
45626         if(this.activePanel && this.activePanel != panel){
45627             this.activePanel.setActiveState(false);
45628         }
45629         this.activePanel = panel;
45630         panel.setActiveState(true);
45631         if(this.panelSize){
45632             panel.setSize(this.panelSize.width, this.panelSize.height);
45633         }
45634         if(this.closeBtn){
45635             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45636         }
45637         this.updateTitle(panel.getTitle());
45638         if(this.tabs){
45639             this.fireEvent("invalidated", this);
45640         }
45641         this.fireEvent("panelactivated", this, panel);
45642     },
45643
45644     /**
45645      * Shows the specified panel.
45646      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45647      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45648      */
45649     showPanel : function(panel){
45650         if(panel = this.getPanel(panel)){
45651             if(this.tabs){
45652                 var tab = this.tabs.getTab(panel.getEl().id);
45653                 if(tab.isHidden()){
45654                     this.tabs.unhideTab(tab.id);
45655                 }
45656                 tab.activate();
45657             }else{
45658                 this.setActivePanel(panel);
45659             }
45660         }
45661         return panel;
45662     },
45663
45664     /**
45665      * Get the active panel for this region.
45666      * @return {Roo.ContentPanel} The active panel or null
45667      */
45668     getActivePanel : function(){
45669         return this.activePanel;
45670     },
45671
45672     validateVisibility : function(){
45673         if(this.panels.getCount() < 1){
45674             this.updateTitle("&#160;");
45675             this.closeBtn.hide();
45676             this.hide();
45677         }else{
45678             if(!this.isVisible()){
45679                 this.show();
45680             }
45681         }
45682     },
45683
45684     /**
45685      * Adds the passed ContentPanel(s) to this region.
45686      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45687      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45688      */
45689     add : function(panel){
45690         if(arguments.length > 1){
45691             for(var i = 0, len = arguments.length; i < len; i++) {
45692                 this.add(arguments[i]);
45693             }
45694             return null;
45695         }
45696         if(this.hasPanel(panel)){
45697             this.showPanel(panel);
45698             return panel;
45699         }
45700         panel.setRegion(this);
45701         this.panels.add(panel);
45702         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45703             this.bodyEl.dom.appendChild(panel.getEl().dom);
45704             if(panel.background !== true){
45705                 this.setActivePanel(panel);
45706             }
45707             this.fireEvent("paneladded", this, panel);
45708             return panel;
45709         }
45710         if(!this.tabs){
45711             this.initTabs();
45712         }else{
45713             this.initPanelAsTab(panel);
45714         }
45715         if(panel.background !== true){
45716             this.tabs.activate(panel.getEl().id);
45717         }
45718         this.fireEvent("paneladded", this, panel);
45719         return panel;
45720     },
45721
45722     /**
45723      * Hides the tab for the specified panel.
45724      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45725      */
45726     hidePanel : function(panel){
45727         if(this.tabs && (panel = this.getPanel(panel))){
45728             this.tabs.hideTab(panel.getEl().id);
45729         }
45730     },
45731
45732     /**
45733      * Unhides the tab for a previously hidden panel.
45734      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45735      */
45736     unhidePanel : function(panel){
45737         if(this.tabs && (panel = this.getPanel(panel))){
45738             this.tabs.unhideTab(panel.getEl().id);
45739         }
45740     },
45741
45742     clearPanels : function(){
45743         while(this.panels.getCount() > 0){
45744              this.remove(this.panels.first());
45745         }
45746     },
45747
45748     /**
45749      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45750      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45751      * @param {Boolean} preservePanel Overrides the config preservePanel option
45752      * @return {Roo.ContentPanel} The panel that was removed
45753      */
45754     remove : function(panel, preservePanel){
45755         panel = this.getPanel(panel);
45756         if(!panel){
45757             return null;
45758         }
45759         var e = {};
45760         this.fireEvent("beforeremove", this, panel, e);
45761         if(e.cancel === true){
45762             return null;
45763         }
45764         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45765         var panelId = panel.getId();
45766         this.panels.removeKey(panelId);
45767         if(preservePanel){
45768             document.body.appendChild(panel.getEl().dom);
45769         }
45770         if(this.tabs){
45771             this.tabs.removeTab(panel.getEl().id);
45772         }else if (!preservePanel){
45773             this.bodyEl.dom.removeChild(panel.getEl().dom);
45774         }
45775         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45776             var p = this.panels.first();
45777             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45778             tempEl.appendChild(p.getEl().dom);
45779             this.bodyEl.update("");
45780             this.bodyEl.dom.appendChild(p.getEl().dom);
45781             tempEl = null;
45782             this.updateTitle(p.getTitle());
45783             this.tabs = null;
45784             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45785             this.setActivePanel(p);
45786         }
45787         panel.setRegion(null);
45788         if(this.activePanel == panel){
45789             this.activePanel = null;
45790         }
45791         if(this.config.autoDestroy !== false && preservePanel !== true){
45792             try{panel.destroy();}catch(e){}
45793         }
45794         this.fireEvent("panelremoved", this, panel);
45795         return panel;
45796     },
45797
45798     /**
45799      * Returns the TabPanel component used by this region
45800      * @return {Roo.TabPanel}
45801      */
45802     getTabs : function(){
45803         return this.tabs;
45804     },
45805
45806     createTool : function(parentEl, className){
45807         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45808             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45809         btn.addClassOnOver("x-layout-tools-button-over");
45810         return btn;
45811     }
45812 });/*
45813  * Based on:
45814  * Ext JS Library 1.1.1
45815  * Copyright(c) 2006-2007, Ext JS, LLC.
45816  *
45817  * Originally Released Under LGPL - original licence link has changed is not relivant.
45818  *
45819  * Fork - LGPL
45820  * <script type="text/javascript">
45821  */
45822  
45823
45824
45825 /**
45826  * @class Roo.SplitLayoutRegion
45827  * @extends Roo.LayoutRegion
45828  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45829  */
45830 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45831     this.cursor = cursor;
45832     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45833 };
45834
45835 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45836     splitTip : "Drag to resize.",
45837     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45838     useSplitTips : false,
45839
45840     applyConfig : function(config){
45841         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45842         if(config.split){
45843             if(!this.split){
45844                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45845                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45846                 /** The SplitBar for this region 
45847                 * @type Roo.SplitBar */
45848                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45849                 this.split.on("moved", this.onSplitMove, this);
45850                 this.split.useShim = config.useShim === true;
45851                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45852                 if(this.useSplitTips){
45853                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45854                 }
45855                 if(config.collapsible){
45856                     this.split.el.on("dblclick", this.collapse,  this);
45857                 }
45858             }
45859             if(typeof config.minSize != "undefined"){
45860                 this.split.minSize = config.minSize;
45861             }
45862             if(typeof config.maxSize != "undefined"){
45863                 this.split.maxSize = config.maxSize;
45864             }
45865             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45866                 this.hideSplitter();
45867             }
45868         }
45869     },
45870
45871     getHMaxSize : function(){
45872          var cmax = this.config.maxSize || 10000;
45873          var center = this.mgr.getRegion("center");
45874          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45875     },
45876
45877     getVMaxSize : function(){
45878          var cmax = this.config.maxSize || 10000;
45879          var center = this.mgr.getRegion("center");
45880          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45881     },
45882
45883     onSplitMove : function(split, newSize){
45884         this.fireEvent("resized", this, newSize);
45885     },
45886     
45887     /** 
45888      * Returns the {@link Roo.SplitBar} for this region.
45889      * @return {Roo.SplitBar}
45890      */
45891     getSplitBar : function(){
45892         return this.split;
45893     },
45894     
45895     hide : function(){
45896         this.hideSplitter();
45897         Roo.SplitLayoutRegion.superclass.hide.call(this);
45898     },
45899
45900     hideSplitter : function(){
45901         if(this.split){
45902             this.split.el.setLocation(-2000,-2000);
45903             this.split.el.hide();
45904         }
45905     },
45906
45907     show : function(){
45908         if(this.split){
45909             this.split.el.show();
45910         }
45911         Roo.SplitLayoutRegion.superclass.show.call(this);
45912     },
45913     
45914     beforeSlide: function(){
45915         if(Roo.isGecko){// firefox overflow auto bug workaround
45916             this.bodyEl.clip();
45917             if(this.tabs) this.tabs.bodyEl.clip();
45918             if(this.activePanel){
45919                 this.activePanel.getEl().clip();
45920                 
45921                 if(this.activePanel.beforeSlide){
45922                     this.activePanel.beforeSlide();
45923                 }
45924             }
45925         }
45926     },
45927     
45928     afterSlide : function(){
45929         if(Roo.isGecko){// firefox overflow auto bug workaround
45930             this.bodyEl.unclip();
45931             if(this.tabs) this.tabs.bodyEl.unclip();
45932             if(this.activePanel){
45933                 this.activePanel.getEl().unclip();
45934                 if(this.activePanel.afterSlide){
45935                     this.activePanel.afterSlide();
45936                 }
45937             }
45938         }
45939     },
45940
45941     initAutoHide : function(){
45942         if(this.autoHide !== false){
45943             if(!this.autoHideHd){
45944                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45945                 this.autoHideHd = {
45946                     "mouseout": function(e){
45947                         if(!e.within(this.el, true)){
45948                             st.delay(500);
45949                         }
45950                     },
45951                     "mouseover" : function(e){
45952                         st.cancel();
45953                     },
45954                     scope : this
45955                 };
45956             }
45957             this.el.on(this.autoHideHd);
45958         }
45959     },
45960
45961     clearAutoHide : function(){
45962         if(this.autoHide !== false){
45963             this.el.un("mouseout", this.autoHideHd.mouseout);
45964             this.el.un("mouseover", this.autoHideHd.mouseover);
45965         }
45966     },
45967
45968     clearMonitor : function(){
45969         Roo.get(document).un("click", this.slideInIf, this);
45970     },
45971
45972     // these names are backwards but not changed for compat
45973     slideOut : function(){
45974         if(this.isSlid || this.el.hasActiveFx()){
45975             return;
45976         }
45977         this.isSlid = true;
45978         if(this.collapseBtn){
45979             this.collapseBtn.hide();
45980         }
45981         this.closeBtnState = this.closeBtn.getStyle('display');
45982         this.closeBtn.hide();
45983         if(this.stickBtn){
45984             this.stickBtn.show();
45985         }
45986         this.el.show();
45987         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45988         this.beforeSlide();
45989         this.el.setStyle("z-index", 10001);
45990         this.el.slideIn(this.getSlideAnchor(), {
45991             callback: function(){
45992                 this.afterSlide();
45993                 this.initAutoHide();
45994                 Roo.get(document).on("click", this.slideInIf, this);
45995                 this.fireEvent("slideshow", this);
45996             },
45997             scope: this,
45998             block: true
45999         });
46000     },
46001
46002     afterSlideIn : function(){
46003         this.clearAutoHide();
46004         this.isSlid = false;
46005         this.clearMonitor();
46006         this.el.setStyle("z-index", "");
46007         if(this.collapseBtn){
46008             this.collapseBtn.show();
46009         }
46010         this.closeBtn.setStyle('display', this.closeBtnState);
46011         if(this.stickBtn){
46012             this.stickBtn.hide();
46013         }
46014         this.fireEvent("slidehide", this);
46015     },
46016
46017     slideIn : function(cb){
46018         if(!this.isSlid || this.el.hasActiveFx()){
46019             Roo.callback(cb);
46020             return;
46021         }
46022         this.isSlid = false;
46023         this.beforeSlide();
46024         this.el.slideOut(this.getSlideAnchor(), {
46025             callback: function(){
46026                 this.el.setLeftTop(-10000, -10000);
46027                 this.afterSlide();
46028                 this.afterSlideIn();
46029                 Roo.callback(cb);
46030             },
46031             scope: this,
46032             block: true
46033         });
46034     },
46035     
46036     slideInIf : function(e){
46037         if(!e.within(this.el)){
46038             this.slideIn();
46039         }
46040     },
46041
46042     animateCollapse : function(){
46043         this.beforeSlide();
46044         this.el.setStyle("z-index", 20000);
46045         var anchor = this.getSlideAnchor();
46046         this.el.slideOut(anchor, {
46047             callback : function(){
46048                 this.el.setStyle("z-index", "");
46049                 this.collapsedEl.slideIn(anchor, {duration:.3});
46050                 this.afterSlide();
46051                 this.el.setLocation(-10000,-10000);
46052                 this.el.hide();
46053                 this.fireEvent("collapsed", this);
46054             },
46055             scope: this,
46056             block: true
46057         });
46058     },
46059
46060     animateExpand : function(){
46061         this.beforeSlide();
46062         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46063         this.el.setStyle("z-index", 20000);
46064         this.collapsedEl.hide({
46065             duration:.1
46066         });
46067         this.el.slideIn(this.getSlideAnchor(), {
46068             callback : function(){
46069                 this.el.setStyle("z-index", "");
46070                 this.afterSlide();
46071                 if(this.split){
46072                     this.split.el.show();
46073                 }
46074                 this.fireEvent("invalidated", this);
46075                 this.fireEvent("expanded", this);
46076             },
46077             scope: this,
46078             block: true
46079         });
46080     },
46081
46082     anchors : {
46083         "west" : "left",
46084         "east" : "right",
46085         "north" : "top",
46086         "south" : "bottom"
46087     },
46088
46089     sanchors : {
46090         "west" : "l",
46091         "east" : "r",
46092         "north" : "t",
46093         "south" : "b"
46094     },
46095
46096     canchors : {
46097         "west" : "tl-tr",
46098         "east" : "tr-tl",
46099         "north" : "tl-bl",
46100         "south" : "bl-tl"
46101     },
46102
46103     getAnchor : function(){
46104         return this.anchors[this.position];
46105     },
46106
46107     getCollapseAnchor : function(){
46108         return this.canchors[this.position];
46109     },
46110
46111     getSlideAnchor : function(){
46112         return this.sanchors[this.position];
46113     },
46114
46115     getAlignAdj : function(){
46116         var cm = this.cmargins;
46117         switch(this.position){
46118             case "west":
46119                 return [0, 0];
46120             break;
46121             case "east":
46122                 return [0, 0];
46123             break;
46124             case "north":
46125                 return [0, 0];
46126             break;
46127             case "south":
46128                 return [0, 0];
46129             break;
46130         }
46131     },
46132
46133     getExpandAdj : function(){
46134         var c = this.collapsedEl, cm = this.cmargins;
46135         switch(this.position){
46136             case "west":
46137                 return [-(cm.right+c.getWidth()+cm.left), 0];
46138             break;
46139             case "east":
46140                 return [cm.right+c.getWidth()+cm.left, 0];
46141             break;
46142             case "north":
46143                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46144             break;
46145             case "south":
46146                 return [0, cm.top+cm.bottom+c.getHeight()];
46147             break;
46148         }
46149     }
46150 });/*
46151  * Based on:
46152  * Ext JS Library 1.1.1
46153  * Copyright(c) 2006-2007, Ext JS, LLC.
46154  *
46155  * Originally Released Under LGPL - original licence link has changed is not relivant.
46156  *
46157  * Fork - LGPL
46158  * <script type="text/javascript">
46159  */
46160 /*
46161  * These classes are private internal classes
46162  */
46163 Roo.CenterLayoutRegion = function(mgr, config){
46164     Roo.LayoutRegion.call(this, mgr, config, "center");
46165     this.visible = true;
46166     this.minWidth = config.minWidth || 20;
46167     this.minHeight = config.minHeight || 20;
46168 };
46169
46170 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46171     hide : function(){
46172         // center panel can't be hidden
46173     },
46174     
46175     show : function(){
46176         // center panel can't be hidden
46177     },
46178     
46179     getMinWidth: function(){
46180         return this.minWidth;
46181     },
46182     
46183     getMinHeight: function(){
46184         return this.minHeight;
46185     }
46186 });
46187
46188
46189 Roo.NorthLayoutRegion = function(mgr, config){
46190     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46191     if(this.split){
46192         this.split.placement = Roo.SplitBar.TOP;
46193         this.split.orientation = Roo.SplitBar.VERTICAL;
46194         this.split.el.addClass("x-layout-split-v");
46195     }
46196     var size = config.initialSize || config.height;
46197     if(typeof size != "undefined"){
46198         this.el.setHeight(size);
46199     }
46200 };
46201 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46202     orientation: Roo.SplitBar.VERTICAL,
46203     getBox : function(){
46204         if(this.collapsed){
46205             return this.collapsedEl.getBox();
46206         }
46207         var box = this.el.getBox();
46208         if(this.split){
46209             box.height += this.split.el.getHeight();
46210         }
46211         return box;
46212     },
46213     
46214     updateBox : function(box){
46215         if(this.split && !this.collapsed){
46216             box.height -= this.split.el.getHeight();
46217             this.split.el.setLeft(box.x);
46218             this.split.el.setTop(box.y+box.height);
46219             this.split.el.setWidth(box.width);
46220         }
46221         if(this.collapsed){
46222             this.updateBody(box.width, null);
46223         }
46224         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46225     }
46226 });
46227
46228 Roo.SouthLayoutRegion = function(mgr, config){
46229     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46230     if(this.split){
46231         this.split.placement = Roo.SplitBar.BOTTOM;
46232         this.split.orientation = Roo.SplitBar.VERTICAL;
46233         this.split.el.addClass("x-layout-split-v");
46234     }
46235     var size = config.initialSize || config.height;
46236     if(typeof size != "undefined"){
46237         this.el.setHeight(size);
46238     }
46239 };
46240 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46241     orientation: Roo.SplitBar.VERTICAL,
46242     getBox : function(){
46243         if(this.collapsed){
46244             return this.collapsedEl.getBox();
46245         }
46246         var box = this.el.getBox();
46247         if(this.split){
46248             var sh = this.split.el.getHeight();
46249             box.height += sh;
46250             box.y -= sh;
46251         }
46252         return box;
46253     },
46254     
46255     updateBox : function(box){
46256         if(this.split && !this.collapsed){
46257             var sh = this.split.el.getHeight();
46258             box.height -= sh;
46259             box.y += sh;
46260             this.split.el.setLeft(box.x);
46261             this.split.el.setTop(box.y-sh);
46262             this.split.el.setWidth(box.width);
46263         }
46264         if(this.collapsed){
46265             this.updateBody(box.width, null);
46266         }
46267         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46268     }
46269 });
46270
46271 Roo.EastLayoutRegion = function(mgr, config){
46272     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46273     if(this.split){
46274         this.split.placement = Roo.SplitBar.RIGHT;
46275         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46276         this.split.el.addClass("x-layout-split-h");
46277     }
46278     var size = config.initialSize || config.width;
46279     if(typeof size != "undefined"){
46280         this.el.setWidth(size);
46281     }
46282 };
46283 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46284     orientation: Roo.SplitBar.HORIZONTAL,
46285     getBox : function(){
46286         if(this.collapsed){
46287             return this.collapsedEl.getBox();
46288         }
46289         var box = this.el.getBox();
46290         if(this.split){
46291             var sw = this.split.el.getWidth();
46292             box.width += sw;
46293             box.x -= sw;
46294         }
46295         return box;
46296     },
46297
46298     updateBox : function(box){
46299         if(this.split && !this.collapsed){
46300             var sw = this.split.el.getWidth();
46301             box.width -= sw;
46302             this.split.el.setLeft(box.x);
46303             this.split.el.setTop(box.y);
46304             this.split.el.setHeight(box.height);
46305             box.x += sw;
46306         }
46307         if(this.collapsed){
46308             this.updateBody(null, box.height);
46309         }
46310         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46311     }
46312 });
46313
46314 Roo.WestLayoutRegion = function(mgr, config){
46315     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46316     if(this.split){
46317         this.split.placement = Roo.SplitBar.LEFT;
46318         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46319         this.split.el.addClass("x-layout-split-h");
46320     }
46321     var size = config.initialSize || config.width;
46322     if(typeof size != "undefined"){
46323         this.el.setWidth(size);
46324     }
46325 };
46326 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46327     orientation: Roo.SplitBar.HORIZONTAL,
46328     getBox : function(){
46329         if(this.collapsed){
46330             return this.collapsedEl.getBox();
46331         }
46332         var box = this.el.getBox();
46333         if(this.split){
46334             box.width += this.split.el.getWidth();
46335         }
46336         return box;
46337     },
46338     
46339     updateBox : function(box){
46340         if(this.split && !this.collapsed){
46341             var sw = this.split.el.getWidth();
46342             box.width -= sw;
46343             this.split.el.setLeft(box.x+box.width);
46344             this.split.el.setTop(box.y);
46345             this.split.el.setHeight(box.height);
46346         }
46347         if(this.collapsed){
46348             this.updateBody(null, box.height);
46349         }
46350         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46351     }
46352 });
46353 /*
46354  * Based on:
46355  * Ext JS Library 1.1.1
46356  * Copyright(c) 2006-2007, Ext JS, LLC.
46357  *
46358  * Originally Released Under LGPL - original licence link has changed is not relivant.
46359  *
46360  * Fork - LGPL
46361  * <script type="text/javascript">
46362  */
46363  
46364  
46365 /*
46366  * Private internal class for reading and applying state
46367  */
46368 Roo.LayoutStateManager = function(layout){
46369      // default empty state
46370      this.state = {
46371         north: {},
46372         south: {},
46373         east: {},
46374         west: {}       
46375     };
46376 };
46377
46378 Roo.LayoutStateManager.prototype = {
46379     init : function(layout, provider){
46380         this.provider = provider;
46381         var state = provider.get(layout.id+"-layout-state");
46382         if(state){
46383             var wasUpdating = layout.isUpdating();
46384             if(!wasUpdating){
46385                 layout.beginUpdate();
46386             }
46387             for(var key in state){
46388                 if(typeof state[key] != "function"){
46389                     var rstate = state[key];
46390                     var r = layout.getRegion(key);
46391                     if(r && rstate){
46392                         if(rstate.size){
46393                             r.resizeTo(rstate.size);
46394                         }
46395                         if(rstate.collapsed == true){
46396                             r.collapse(true);
46397                         }else{
46398                             r.expand(null, true);
46399                         }
46400                     }
46401                 }
46402             }
46403             if(!wasUpdating){
46404                 layout.endUpdate();
46405             }
46406             this.state = state; 
46407         }
46408         this.layout = layout;
46409         layout.on("regionresized", this.onRegionResized, this);
46410         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46411         layout.on("regionexpanded", this.onRegionExpanded, this);
46412     },
46413     
46414     storeState : function(){
46415         this.provider.set(this.layout.id+"-layout-state", this.state);
46416     },
46417     
46418     onRegionResized : function(region, newSize){
46419         this.state[region.getPosition()].size = newSize;
46420         this.storeState();
46421     },
46422     
46423     onRegionCollapsed : function(region){
46424         this.state[region.getPosition()].collapsed = true;
46425         this.storeState();
46426     },
46427     
46428     onRegionExpanded : function(region){
46429         this.state[region.getPosition()].collapsed = false;
46430         this.storeState();
46431     }
46432 };/*
46433  * Based on:
46434  * Ext JS Library 1.1.1
46435  * Copyright(c) 2006-2007, Ext JS, LLC.
46436  *
46437  * Originally Released Under LGPL - original licence link has changed is not relivant.
46438  *
46439  * Fork - LGPL
46440  * <script type="text/javascript">
46441  */
46442 /**
46443  * @class Roo.ContentPanel
46444  * @extends Roo.util.Observable
46445  * A basic ContentPanel element.
46446  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46447  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46448  * @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
46449  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46450  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46451  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46452  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46453  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46454  * @cfg {String} title          The title for this panel
46455  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46456  * @cfg {String} url            Calls {@link #setUrl} with this value
46457  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46458  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46459  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46460  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46461
46462  * @constructor
46463  * Create a new ContentPanel.
46464  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46465  * @param {String/Object} config A string to set only the title or a config object
46466  * @param {String} content (optional) Set the HTML content for this panel
46467  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46468  */
46469 Roo.ContentPanel = function(el, config, content){
46470     
46471      
46472     /*
46473     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46474         config = el;
46475         el = Roo.id();
46476     }
46477     if (config && config.parentLayout) { 
46478         el = config.parentLayout.el.createChild(); 
46479     }
46480     */
46481     if(el.autoCreate){ // xtype is available if this is called from factory
46482         config = el;
46483         el = Roo.id();
46484     }
46485     this.el = Roo.get(el);
46486     if(!this.el && config && config.autoCreate){
46487         if(typeof config.autoCreate == "object"){
46488             if(!config.autoCreate.id){
46489                 config.autoCreate.id = config.id||el;
46490             }
46491             this.el = Roo.DomHelper.append(document.body,
46492                         config.autoCreate, true);
46493         }else{
46494             this.el = Roo.DomHelper.append(document.body,
46495                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46496         }
46497     }
46498     this.closable = false;
46499     this.loaded = false;
46500     this.active = false;
46501     if(typeof config == "string"){
46502         this.title = config;
46503     }else{
46504         Roo.apply(this, config);
46505     }
46506     
46507     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46508         this.wrapEl = this.el.wrap();
46509         this.toolbar.container = this.el.insertSibling(false, 'before');
46510         this.toolbar = new Roo.Toolbar(this.toolbar);
46511     }
46512     
46513     
46514     
46515     if(this.resizeEl){
46516         this.resizeEl = Roo.get(this.resizeEl, true);
46517     }else{
46518         this.resizeEl = this.el;
46519     }
46520     this.addEvents({
46521         /**
46522          * @event activate
46523          * Fires when this panel is activated. 
46524          * @param {Roo.ContentPanel} this
46525          */
46526         "activate" : true,
46527         /**
46528          * @event deactivate
46529          * Fires when this panel is activated. 
46530          * @param {Roo.ContentPanel} this
46531          */
46532         "deactivate" : true,
46533
46534         /**
46535          * @event resize
46536          * Fires when this panel is resized if fitToFrame is true.
46537          * @param {Roo.ContentPanel} this
46538          * @param {Number} width The width after any component adjustments
46539          * @param {Number} height The height after any component adjustments
46540          */
46541         "resize" : true,
46542         
46543          /**
46544          * @event render
46545          * Fires when this tab is created
46546          * @param {Roo.ContentPanel} this
46547          */
46548         "render" : true
46549         
46550         
46551         
46552     });
46553     if(this.autoScroll){
46554         this.resizeEl.setStyle("overflow", "auto");
46555     } else {
46556         // fix randome scrolling
46557         this.el.on('scroll', function() {
46558             Roo.log('fix random scolling');
46559             this.scrollTo('top',0); 
46560         });
46561     }
46562     content = content || this.content;
46563     if(content){
46564         this.setContent(content);
46565     }
46566     if(config && config.url){
46567         this.setUrl(this.url, this.params, this.loadOnce);
46568     }
46569     
46570     
46571     
46572     Roo.ContentPanel.superclass.constructor.call(this);
46573     
46574     this.fireEvent('render', this);
46575 };
46576
46577 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46578     tabTip:'',
46579     setRegion : function(region){
46580         this.region = region;
46581         if(region){
46582            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46583         }else{
46584            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46585         } 
46586     },
46587     
46588     /**
46589      * Returns the toolbar for this Panel if one was configured. 
46590      * @return {Roo.Toolbar} 
46591      */
46592     getToolbar : function(){
46593         return this.toolbar;
46594     },
46595     
46596     setActiveState : function(active){
46597         this.active = active;
46598         if(!active){
46599             this.fireEvent("deactivate", this);
46600         }else{
46601             this.fireEvent("activate", this);
46602         }
46603     },
46604     /**
46605      * Updates this panel's element
46606      * @param {String} content The new content
46607      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46608     */
46609     setContent : function(content, loadScripts){
46610         this.el.update(content, loadScripts);
46611     },
46612
46613     ignoreResize : function(w, h){
46614         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46615             return true;
46616         }else{
46617             this.lastSize = {width: w, height: h};
46618             return false;
46619         }
46620     },
46621     /**
46622      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46623      * @return {Roo.UpdateManager} The UpdateManager
46624      */
46625     getUpdateManager : function(){
46626         return this.el.getUpdateManager();
46627     },
46628      /**
46629      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46630      * @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:
46631 <pre><code>
46632 panel.load({
46633     url: "your-url.php",
46634     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46635     callback: yourFunction,
46636     scope: yourObject, //(optional scope)
46637     discardUrl: false,
46638     nocache: false,
46639     text: "Loading...",
46640     timeout: 30,
46641     scripts: false
46642 });
46643 </code></pre>
46644      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46645      * 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.
46646      * @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}
46647      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46648      * @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.
46649      * @return {Roo.ContentPanel} this
46650      */
46651     load : function(){
46652         var um = this.el.getUpdateManager();
46653         um.update.apply(um, arguments);
46654         return this;
46655     },
46656
46657
46658     /**
46659      * 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.
46660      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46661      * @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)
46662      * @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)
46663      * @return {Roo.UpdateManager} The UpdateManager
46664      */
46665     setUrl : function(url, params, loadOnce){
46666         if(this.refreshDelegate){
46667             this.removeListener("activate", this.refreshDelegate);
46668         }
46669         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46670         this.on("activate", this.refreshDelegate);
46671         return this.el.getUpdateManager();
46672     },
46673     
46674     _handleRefresh : function(url, params, loadOnce){
46675         if(!loadOnce || !this.loaded){
46676             var updater = this.el.getUpdateManager();
46677             updater.update(url, params, this._setLoaded.createDelegate(this));
46678         }
46679     },
46680     
46681     _setLoaded : function(){
46682         this.loaded = true;
46683     }, 
46684     
46685     /**
46686      * Returns this panel's id
46687      * @return {String} 
46688      */
46689     getId : function(){
46690         return this.el.id;
46691     },
46692     
46693     /** 
46694      * Returns this panel's element - used by regiosn to add.
46695      * @return {Roo.Element} 
46696      */
46697     getEl : function(){
46698         return this.wrapEl || this.el;
46699     },
46700     
46701     adjustForComponents : function(width, height){
46702         if(this.resizeEl != this.el){
46703             width -= this.el.getFrameWidth('lr');
46704             height -= this.el.getFrameWidth('tb');
46705         }
46706         if(this.toolbar){
46707             var te = this.toolbar.getEl();
46708             height -= te.getHeight();
46709             te.setWidth(width);
46710         }
46711         if(this.adjustments){
46712             width += this.adjustments[0];
46713             height += this.adjustments[1];
46714         }
46715         return {"width": width, "height": height};
46716     },
46717     
46718     setSize : function(width, height){
46719         if(this.fitToFrame && !this.ignoreResize(width, height)){
46720             if(this.fitContainer && this.resizeEl != this.el){
46721                 this.el.setSize(width, height);
46722             }
46723             var size = this.adjustForComponents(width, height);
46724             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46725             this.fireEvent('resize', this, size.width, size.height);
46726         }
46727     },
46728     
46729     /**
46730      * Returns this panel's title
46731      * @return {String} 
46732      */
46733     getTitle : function(){
46734         return this.title;
46735     },
46736     
46737     /**
46738      * Set this panel's title
46739      * @param {String} title
46740      */
46741     setTitle : function(title){
46742         this.title = title;
46743         if(this.region){
46744             this.region.updatePanelTitle(this, title);
46745         }
46746     },
46747     
46748     /**
46749      * Returns true is this panel was configured to be closable
46750      * @return {Boolean} 
46751      */
46752     isClosable : function(){
46753         return this.closable;
46754     },
46755     
46756     beforeSlide : function(){
46757         this.el.clip();
46758         this.resizeEl.clip();
46759     },
46760     
46761     afterSlide : function(){
46762         this.el.unclip();
46763         this.resizeEl.unclip();
46764     },
46765     
46766     /**
46767      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46768      *   Will fail silently if the {@link #setUrl} method has not been called.
46769      *   This does not activate the panel, just updates its content.
46770      */
46771     refresh : function(){
46772         if(this.refreshDelegate){
46773            this.loaded = false;
46774            this.refreshDelegate();
46775         }
46776     },
46777     
46778     /**
46779      * Destroys this panel
46780      */
46781     destroy : function(){
46782         this.el.removeAllListeners();
46783         var tempEl = document.createElement("span");
46784         tempEl.appendChild(this.el.dom);
46785         tempEl.innerHTML = "";
46786         this.el.remove();
46787         this.el = null;
46788     },
46789     
46790     /**
46791      * form - if the content panel contains a form - this is a reference to it.
46792      * @type {Roo.form.Form}
46793      */
46794     form : false,
46795     /**
46796      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46797      *    This contains a reference to it.
46798      * @type {Roo.View}
46799      */
46800     view : false,
46801     
46802       /**
46803      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46804      * <pre><code>
46805
46806 layout.addxtype({
46807        xtype : 'Form',
46808        items: [ .... ]
46809    }
46810 );
46811
46812 </code></pre>
46813      * @param {Object} cfg Xtype definition of item to add.
46814      */
46815     
46816     addxtype : function(cfg) {
46817         // add form..
46818         if (cfg.xtype.match(/^Form$/)) {
46819             var el = this.el.createChild();
46820
46821             this.form = new  Roo.form.Form(cfg);
46822             
46823             
46824             if ( this.form.allItems.length) this.form.render(el.dom);
46825             return this.form;
46826         }
46827         // should only have one of theses..
46828         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46829             // views..
46830             cfg.el = this.el.appendChild(document.createElement("div"));
46831             // factory?
46832             
46833             var ret = new Roo.factory(cfg);
46834             ret.render && ret.render(false, ''); // render blank..
46835             this.view = ret;
46836             return ret;
46837         }
46838         return false;
46839     }
46840 });
46841
46842 /**
46843  * @class Roo.GridPanel
46844  * @extends Roo.ContentPanel
46845  * @constructor
46846  * Create a new GridPanel.
46847  * @param {Roo.grid.Grid} grid The grid for this panel
46848  * @param {String/Object} config A string to set only the panel's title, or a config object
46849  */
46850 Roo.GridPanel = function(grid, config){
46851     
46852   
46853     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46854         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46855         
46856     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46857     
46858     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46859     
46860     if(this.toolbar){
46861         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46862     }
46863     // xtype created footer. - not sure if will work as we normally have to render first..
46864     if (this.footer && !this.footer.el && this.footer.xtype) {
46865         
46866         this.footer.container = this.grid.getView().getFooterPanel(true);
46867         this.footer.dataSource = this.grid.dataSource;
46868         this.footer = Roo.factory(this.footer, Roo);
46869         
46870     }
46871     
46872     grid.monitorWindowResize = false; // turn off autosizing
46873     grid.autoHeight = false;
46874     grid.autoWidth = false;
46875     this.grid = grid;
46876     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46877 };
46878
46879 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46880     getId : function(){
46881         return this.grid.id;
46882     },
46883     
46884     /**
46885      * Returns the grid for this panel
46886      * @return {Roo.grid.Grid} 
46887      */
46888     getGrid : function(){
46889         return this.grid;    
46890     },
46891     
46892     setSize : function(width, height){
46893         if(!this.ignoreResize(width, height)){
46894             var grid = this.grid;
46895             var size = this.adjustForComponents(width, height);
46896             grid.getGridEl().setSize(size.width, size.height);
46897             grid.autoSize();
46898         }
46899     },
46900     
46901     beforeSlide : function(){
46902         this.grid.getView().scroller.clip();
46903     },
46904     
46905     afterSlide : function(){
46906         this.grid.getView().scroller.unclip();
46907     },
46908     
46909     destroy : function(){
46910         this.grid.destroy();
46911         delete this.grid;
46912         Roo.GridPanel.superclass.destroy.call(this); 
46913     }
46914 });
46915
46916
46917 /**
46918  * @class Roo.NestedLayoutPanel
46919  * @extends Roo.ContentPanel
46920  * @constructor
46921  * Create a new NestedLayoutPanel.
46922  * 
46923  * 
46924  * @param {Roo.BorderLayout} layout The layout for this panel
46925  * @param {String/Object} config A string to set only the title or a config object
46926  */
46927 Roo.NestedLayoutPanel = function(layout, config)
46928 {
46929     // construct with only one argument..
46930     /* FIXME - implement nicer consturctors
46931     if (layout.layout) {
46932         config = layout;
46933         layout = config.layout;
46934         delete config.layout;
46935     }
46936     if (layout.xtype && !layout.getEl) {
46937         // then layout needs constructing..
46938         layout = Roo.factory(layout, Roo);
46939     }
46940     */
46941     
46942     
46943     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46944     
46945     layout.monitorWindowResize = false; // turn off autosizing
46946     this.layout = layout;
46947     this.layout.getEl().addClass("x-layout-nested-layout");
46948     
46949     
46950     
46951     
46952 };
46953
46954 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46955
46956     setSize : function(width, height){
46957         if(!this.ignoreResize(width, height)){
46958             var size = this.adjustForComponents(width, height);
46959             var el = this.layout.getEl();
46960             el.setSize(size.width, size.height);
46961             var touch = el.dom.offsetWidth;
46962             this.layout.layout();
46963             // ie requires a double layout on the first pass
46964             if(Roo.isIE && !this.initialized){
46965                 this.initialized = true;
46966                 this.layout.layout();
46967             }
46968         }
46969     },
46970     
46971     // activate all subpanels if not currently active..
46972     
46973     setActiveState : function(active){
46974         this.active = active;
46975         if(!active){
46976             this.fireEvent("deactivate", this);
46977             return;
46978         }
46979         
46980         this.fireEvent("activate", this);
46981         // not sure if this should happen before or after..
46982         if (!this.layout) {
46983             return; // should not happen..
46984         }
46985         var reg = false;
46986         for (var r in this.layout.regions) {
46987             reg = this.layout.getRegion(r);
46988             if (reg.getActivePanel()) {
46989                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46990                 reg.setActivePanel(reg.getActivePanel());
46991                 continue;
46992             }
46993             if (!reg.panels.length) {
46994                 continue;
46995             }
46996             reg.showPanel(reg.getPanel(0));
46997         }
46998         
46999         
47000         
47001         
47002     },
47003     
47004     /**
47005      * Returns the nested BorderLayout for this panel
47006      * @return {Roo.BorderLayout} 
47007      */
47008     getLayout : function(){
47009         return this.layout;
47010     },
47011     
47012      /**
47013      * Adds a xtype elements to the layout of the nested panel
47014      * <pre><code>
47015
47016 panel.addxtype({
47017        xtype : 'ContentPanel',
47018        region: 'west',
47019        items: [ .... ]
47020    }
47021 );
47022
47023 panel.addxtype({
47024         xtype : 'NestedLayoutPanel',
47025         region: 'west',
47026         layout: {
47027            center: { },
47028            west: { }   
47029         },
47030         items : [ ... list of content panels or nested layout panels.. ]
47031    }
47032 );
47033 </code></pre>
47034      * @param {Object} cfg Xtype definition of item to add.
47035      */
47036     addxtype : function(cfg) {
47037         return this.layout.addxtype(cfg);
47038     
47039     }
47040 });
47041
47042 Roo.ScrollPanel = function(el, config, content){
47043     config = config || {};
47044     config.fitToFrame = true;
47045     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47046     
47047     this.el.dom.style.overflow = "hidden";
47048     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47049     this.el.removeClass("x-layout-inactive-content");
47050     this.el.on("mousewheel", this.onWheel, this);
47051
47052     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47053     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47054     up.unselectable(); down.unselectable();
47055     up.on("click", this.scrollUp, this);
47056     down.on("click", this.scrollDown, this);
47057     up.addClassOnOver("x-scroller-btn-over");
47058     down.addClassOnOver("x-scroller-btn-over");
47059     up.addClassOnClick("x-scroller-btn-click");
47060     down.addClassOnClick("x-scroller-btn-click");
47061     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47062
47063     this.resizeEl = this.el;
47064     this.el = wrap; this.up = up; this.down = down;
47065 };
47066
47067 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47068     increment : 100,
47069     wheelIncrement : 5,
47070     scrollUp : function(){
47071         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47072     },
47073
47074     scrollDown : function(){
47075         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47076     },
47077
47078     afterScroll : function(){
47079         var el = this.resizeEl;
47080         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47081         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47082         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47083     },
47084
47085     setSize : function(){
47086         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47087         this.afterScroll();
47088     },
47089
47090     onWheel : function(e){
47091         var d = e.getWheelDelta();
47092         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47093         this.afterScroll();
47094         e.stopEvent();
47095     },
47096
47097     setContent : function(content, loadScripts){
47098         this.resizeEl.update(content, loadScripts);
47099     }
47100
47101 });
47102
47103
47104
47105
47106
47107
47108
47109
47110
47111 /**
47112  * @class Roo.TreePanel
47113  * @extends Roo.ContentPanel
47114  * @constructor
47115  * Create a new TreePanel. - defaults to fit/scoll contents.
47116  * @param {String/Object} config A string to set only the panel's title, or a config object
47117  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47118  */
47119 Roo.TreePanel = function(config){
47120     var el = config.el;
47121     var tree = config.tree;
47122     delete config.tree; 
47123     delete config.el; // hopefull!
47124     
47125     // wrapper for IE7 strict & safari scroll issue
47126     
47127     var treeEl = el.createChild();
47128     config.resizeEl = treeEl;
47129     
47130     
47131     
47132     Roo.TreePanel.superclass.constructor.call(this, el, config);
47133  
47134  
47135     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47136     //console.log(tree);
47137     this.on('activate', function()
47138     {
47139         if (this.tree.rendered) {
47140             return;
47141         }
47142         //console.log('render tree');
47143         this.tree.render();
47144     });
47145     
47146     this.on('resize',  function (cp, w, h) {
47147             this.tree.innerCt.setWidth(w);
47148             this.tree.innerCt.setHeight(h);
47149             this.tree.innerCt.setStyle('overflow-y', 'auto');
47150     });
47151
47152         
47153     
47154 };
47155
47156 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47157     fitToFrame : true,
47158     autoScroll : true
47159 });
47160
47161
47162
47163
47164
47165
47166
47167
47168
47169
47170
47171 /*
47172  * Based on:
47173  * Ext JS Library 1.1.1
47174  * Copyright(c) 2006-2007, Ext JS, LLC.
47175  *
47176  * Originally Released Under LGPL - original licence link has changed is not relivant.
47177  *
47178  * Fork - LGPL
47179  * <script type="text/javascript">
47180  */
47181  
47182
47183 /**
47184  * @class Roo.ReaderLayout
47185  * @extends Roo.BorderLayout
47186  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47187  * center region containing two nested regions (a top one for a list view and one for item preview below),
47188  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47189  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47190  * expedites the setup of the overall layout and regions for this common application style.
47191  * Example:
47192  <pre><code>
47193 var reader = new Roo.ReaderLayout();
47194 var CP = Roo.ContentPanel;  // shortcut for adding
47195
47196 reader.beginUpdate();
47197 reader.add("north", new CP("north", "North"));
47198 reader.add("west", new CP("west", {title: "West"}));
47199 reader.add("east", new CP("east", {title: "East"}));
47200
47201 reader.regions.listView.add(new CP("listView", "List"));
47202 reader.regions.preview.add(new CP("preview", "Preview"));
47203 reader.endUpdate();
47204 </code></pre>
47205 * @constructor
47206 * Create a new ReaderLayout
47207 * @param {Object} config Configuration options
47208 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47209 * document.body if omitted)
47210 */
47211 Roo.ReaderLayout = function(config, renderTo){
47212     var c = config || {size:{}};
47213     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47214         north: c.north !== false ? Roo.apply({
47215             split:false,
47216             initialSize: 32,
47217             titlebar: false
47218         }, c.north) : false,
47219         west: c.west !== false ? Roo.apply({
47220             split:true,
47221             initialSize: 200,
47222             minSize: 175,
47223             maxSize: 400,
47224             titlebar: true,
47225             collapsible: true,
47226             animate: true,
47227             margins:{left:5,right:0,bottom:5,top:5},
47228             cmargins:{left:5,right:5,bottom:5,top:5}
47229         }, c.west) : false,
47230         east: c.east !== false ? Roo.apply({
47231             split:true,
47232             initialSize: 200,
47233             minSize: 175,
47234             maxSize: 400,
47235             titlebar: true,
47236             collapsible: true,
47237             animate: true,
47238             margins:{left:0,right:5,bottom:5,top:5},
47239             cmargins:{left:5,right:5,bottom:5,top:5}
47240         }, c.east) : false,
47241         center: Roo.apply({
47242             tabPosition: 'top',
47243             autoScroll:false,
47244             closeOnTab: true,
47245             titlebar:false,
47246             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47247         }, c.center)
47248     });
47249
47250     this.el.addClass('x-reader');
47251
47252     this.beginUpdate();
47253
47254     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47255         south: c.preview !== false ? Roo.apply({
47256             split:true,
47257             initialSize: 200,
47258             minSize: 100,
47259             autoScroll:true,
47260             collapsible:true,
47261             titlebar: true,
47262             cmargins:{top:5,left:0, right:0, bottom:0}
47263         }, c.preview) : false,
47264         center: Roo.apply({
47265             autoScroll:false,
47266             titlebar:false,
47267             minHeight:200
47268         }, c.listView)
47269     });
47270     this.add('center', new Roo.NestedLayoutPanel(inner,
47271             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47272
47273     this.endUpdate();
47274
47275     this.regions.preview = inner.getRegion('south');
47276     this.regions.listView = inner.getRegion('center');
47277 };
47278
47279 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47280  * Based on:
47281  * Ext JS Library 1.1.1
47282  * Copyright(c) 2006-2007, Ext JS, LLC.
47283  *
47284  * Originally Released Under LGPL - original licence link has changed is not relivant.
47285  *
47286  * Fork - LGPL
47287  * <script type="text/javascript">
47288  */
47289  
47290 /**
47291  * @class Roo.grid.Grid
47292  * @extends Roo.util.Observable
47293  * This class represents the primary interface of a component based grid control.
47294  * <br><br>Usage:<pre><code>
47295  var grid = new Roo.grid.Grid("my-container-id", {
47296      ds: myDataStore,
47297      cm: myColModel,
47298      selModel: mySelectionModel,
47299      autoSizeColumns: true,
47300      monitorWindowResize: false,
47301      trackMouseOver: true
47302  });
47303  // set any options
47304  grid.render();
47305  * </code></pre>
47306  * <b>Common Problems:</b><br/>
47307  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47308  * element will correct this<br/>
47309  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47310  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47311  * are unpredictable.<br/>
47312  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47313  * grid to calculate dimensions/offsets.<br/>
47314   * @constructor
47315  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47316  * The container MUST have some type of size defined for the grid to fill. The container will be
47317  * automatically set to position relative if it isn't already.
47318  * @param {Object} config A config object that sets properties on this grid.
47319  */
47320 Roo.grid.Grid = function(container, config){
47321         // initialize the container
47322         this.container = Roo.get(container);
47323         this.container.update("");
47324         this.container.setStyle("overflow", "hidden");
47325     this.container.addClass('x-grid-container');
47326
47327     this.id = this.container.id;
47328
47329     Roo.apply(this, config);
47330     // check and correct shorthanded configs
47331     if(this.ds){
47332         this.dataSource = this.ds;
47333         delete this.ds;
47334     }
47335     if(this.cm){
47336         this.colModel = this.cm;
47337         delete this.cm;
47338     }
47339     if(this.sm){
47340         this.selModel = this.sm;
47341         delete this.sm;
47342     }
47343
47344     if (this.selModel) {
47345         this.selModel = Roo.factory(this.selModel, Roo.grid);
47346         this.sm = this.selModel;
47347         this.sm.xmodule = this.xmodule || false;
47348     }
47349     if (typeof(this.colModel.config) == 'undefined') {
47350         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47351         this.cm = this.colModel;
47352         this.cm.xmodule = this.xmodule || false;
47353     }
47354     if (this.dataSource) {
47355         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47356         this.ds = this.dataSource;
47357         this.ds.xmodule = this.xmodule || false;
47358          
47359     }
47360     
47361     
47362     
47363     if(this.width){
47364         this.container.setWidth(this.width);
47365     }
47366
47367     if(this.height){
47368         this.container.setHeight(this.height);
47369     }
47370     /** @private */
47371         this.addEvents({
47372         // raw events
47373         /**
47374          * @event click
47375          * The raw click event for the entire grid.
47376          * @param {Roo.EventObject} e
47377          */
47378         "click" : true,
47379         /**
47380          * @event dblclick
47381          * The raw dblclick event for the entire grid.
47382          * @param {Roo.EventObject} e
47383          */
47384         "dblclick" : true,
47385         /**
47386          * @event contextmenu
47387          * The raw contextmenu event for the entire grid.
47388          * @param {Roo.EventObject} e
47389          */
47390         "contextmenu" : true,
47391         /**
47392          * @event mousedown
47393          * The raw mousedown event for the entire grid.
47394          * @param {Roo.EventObject} e
47395          */
47396         "mousedown" : true,
47397         /**
47398          * @event mouseup
47399          * The raw mouseup event for the entire grid.
47400          * @param {Roo.EventObject} e
47401          */
47402         "mouseup" : true,
47403         /**
47404          * @event mouseover
47405          * The raw mouseover event for the entire grid.
47406          * @param {Roo.EventObject} e
47407          */
47408         "mouseover" : true,
47409         /**
47410          * @event mouseout
47411          * The raw mouseout event for the entire grid.
47412          * @param {Roo.EventObject} e
47413          */
47414         "mouseout" : true,
47415         /**
47416          * @event keypress
47417          * The raw keypress event for the entire grid.
47418          * @param {Roo.EventObject} e
47419          */
47420         "keypress" : true,
47421         /**
47422          * @event keydown
47423          * The raw keydown event for the entire grid.
47424          * @param {Roo.EventObject} e
47425          */
47426         "keydown" : true,
47427
47428         // custom events
47429
47430         /**
47431          * @event cellclick
47432          * Fires when a cell is clicked
47433          * @param {Grid} this
47434          * @param {Number} rowIndex
47435          * @param {Number} columnIndex
47436          * @param {Roo.EventObject} e
47437          */
47438         "cellclick" : true,
47439         /**
47440          * @event celldblclick
47441          * Fires when a cell is double clicked
47442          * @param {Grid} this
47443          * @param {Number} rowIndex
47444          * @param {Number} columnIndex
47445          * @param {Roo.EventObject} e
47446          */
47447         "celldblclick" : true,
47448         /**
47449          * @event rowclick
47450          * Fires when a row is clicked
47451          * @param {Grid} this
47452          * @param {Number} rowIndex
47453          * @param {Roo.EventObject} e
47454          */
47455         "rowclick" : true,
47456         /**
47457          * @event rowdblclick
47458          * Fires when a row is double clicked
47459          * @param {Grid} this
47460          * @param {Number} rowIndex
47461          * @param {Roo.EventObject} e
47462          */
47463         "rowdblclick" : true,
47464         /**
47465          * @event headerclick
47466          * Fires when a header is clicked
47467          * @param {Grid} this
47468          * @param {Number} columnIndex
47469          * @param {Roo.EventObject} e
47470          */
47471         "headerclick" : true,
47472         /**
47473          * @event headerdblclick
47474          * Fires when a header cell is double clicked
47475          * @param {Grid} this
47476          * @param {Number} columnIndex
47477          * @param {Roo.EventObject} e
47478          */
47479         "headerdblclick" : true,
47480         /**
47481          * @event rowcontextmenu
47482          * Fires when a row is right clicked
47483          * @param {Grid} this
47484          * @param {Number} rowIndex
47485          * @param {Roo.EventObject} e
47486          */
47487         "rowcontextmenu" : true,
47488         /**
47489          * @event cellcontextmenu
47490          * Fires when a cell is right clicked
47491          * @param {Grid} this
47492          * @param {Number} rowIndex
47493          * @param {Number} cellIndex
47494          * @param {Roo.EventObject} e
47495          */
47496          "cellcontextmenu" : true,
47497         /**
47498          * @event headercontextmenu
47499          * Fires when a header is right clicked
47500          * @param {Grid} this
47501          * @param {Number} columnIndex
47502          * @param {Roo.EventObject} e
47503          */
47504         "headercontextmenu" : true,
47505         /**
47506          * @event bodyscroll
47507          * Fires when the body element is scrolled
47508          * @param {Number} scrollLeft
47509          * @param {Number} scrollTop
47510          */
47511         "bodyscroll" : true,
47512         /**
47513          * @event columnresize
47514          * Fires when the user resizes a column
47515          * @param {Number} columnIndex
47516          * @param {Number} newSize
47517          */
47518         "columnresize" : true,
47519         /**
47520          * @event columnmove
47521          * Fires when the user moves a column
47522          * @param {Number} oldIndex
47523          * @param {Number} newIndex
47524          */
47525         "columnmove" : true,
47526         /**
47527          * @event startdrag
47528          * Fires when row(s) start being dragged
47529          * @param {Grid} this
47530          * @param {Roo.GridDD} dd The drag drop object
47531          * @param {event} e The raw browser event
47532          */
47533         "startdrag" : true,
47534         /**
47535          * @event enddrag
47536          * Fires when a drag operation is complete
47537          * @param {Grid} this
47538          * @param {Roo.GridDD} dd The drag drop object
47539          * @param {event} e The raw browser event
47540          */
47541         "enddrag" : true,
47542         /**
47543          * @event dragdrop
47544          * Fires when dragged row(s) are dropped on a valid DD target
47545          * @param {Grid} this
47546          * @param {Roo.GridDD} dd The drag drop object
47547          * @param {String} targetId The target drag drop object
47548          * @param {event} e The raw browser event
47549          */
47550         "dragdrop" : true,
47551         /**
47552          * @event dragover
47553          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47554          * @param {Grid} this
47555          * @param {Roo.GridDD} dd The drag drop object
47556          * @param {String} targetId The target drag drop object
47557          * @param {event} e The raw browser event
47558          */
47559         "dragover" : true,
47560         /**
47561          * @event dragenter
47562          *  Fires when the dragged row(s) first cross another DD target while being dragged
47563          * @param {Grid} this
47564          * @param {Roo.GridDD} dd The drag drop object
47565          * @param {String} targetId The target drag drop object
47566          * @param {event} e The raw browser event
47567          */
47568         "dragenter" : true,
47569         /**
47570          * @event dragout
47571          * Fires when the dragged row(s) leave another DD target while being dragged
47572          * @param {Grid} this
47573          * @param {Roo.GridDD} dd The drag drop object
47574          * @param {String} targetId The target drag drop object
47575          * @param {event} e The raw browser event
47576          */
47577         "dragout" : true,
47578         /**
47579          * @event rowclass
47580          * Fires when a row is rendered, so you can change add a style to it.
47581          * @param {GridView} gridview   The grid view
47582          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47583          */
47584         'rowclass' : true,
47585
47586         /**
47587          * @event render
47588          * Fires when the grid is rendered
47589          * @param {Grid} grid
47590          */
47591         'render' : true
47592     });
47593
47594     Roo.grid.Grid.superclass.constructor.call(this);
47595 };
47596 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47597     
47598     /**
47599      * @cfg {String} ddGroup - drag drop group.
47600      */
47601
47602     /**
47603      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47604      */
47605     minColumnWidth : 25,
47606
47607     /**
47608      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47609      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47610      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47611      */
47612     autoSizeColumns : false,
47613
47614     /**
47615      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47616      */
47617     autoSizeHeaders : true,
47618
47619     /**
47620      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47621      */
47622     monitorWindowResize : true,
47623
47624     /**
47625      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47626      * rows measured to get a columns size. Default is 0 (all rows).
47627      */
47628     maxRowsToMeasure : 0,
47629
47630     /**
47631      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47632      */
47633     trackMouseOver : true,
47634
47635     /**
47636     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47637     */
47638     
47639     /**
47640     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47641     */
47642     enableDragDrop : false,
47643     
47644     /**
47645     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47646     */
47647     enableColumnMove : true,
47648     
47649     /**
47650     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47651     */
47652     enableColumnHide : true,
47653     
47654     /**
47655     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47656     */
47657     enableRowHeightSync : false,
47658     
47659     /**
47660     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47661     */
47662     stripeRows : true,
47663     
47664     /**
47665     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47666     */
47667     autoHeight : false,
47668
47669     /**
47670      * @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.
47671      */
47672     autoExpandColumn : false,
47673
47674     /**
47675     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47676     * Default is 50.
47677     */
47678     autoExpandMin : 50,
47679
47680     /**
47681     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47682     */
47683     autoExpandMax : 1000,
47684
47685     /**
47686     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47687     */
47688     view : null,
47689
47690     /**
47691     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47692     */
47693     loadMask : false,
47694     /**
47695     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47696     */
47697     dropTarget: false,
47698     
47699    
47700     
47701     // private
47702     rendered : false,
47703
47704     /**
47705     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47706     * of a fixed width. Default is false.
47707     */
47708     /**
47709     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47710     */
47711     /**
47712      * Called once after all setup has been completed and the grid is ready to be rendered.
47713      * @return {Roo.grid.Grid} this
47714      */
47715     render : function()
47716     {
47717         var c = this.container;
47718         // try to detect autoHeight/width mode
47719         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47720             this.autoHeight = true;
47721         }
47722         var view = this.getView();
47723         view.init(this);
47724
47725         c.on("click", this.onClick, this);
47726         c.on("dblclick", this.onDblClick, this);
47727         c.on("contextmenu", this.onContextMenu, this);
47728         c.on("keydown", this.onKeyDown, this);
47729
47730         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47731
47732         this.getSelectionModel().init(this);
47733
47734         view.render();
47735
47736         if(this.loadMask){
47737             this.loadMask = new Roo.LoadMask(this.container,
47738                     Roo.apply({store:this.dataSource}, this.loadMask));
47739         }
47740         
47741         
47742         if (this.toolbar && this.toolbar.xtype) {
47743             this.toolbar.container = this.getView().getHeaderPanel(true);
47744             this.toolbar = new Roo.Toolbar(this.toolbar);
47745         }
47746         if (this.footer && this.footer.xtype) {
47747             this.footer.dataSource = this.getDataSource();
47748             this.footer.container = this.getView().getFooterPanel(true);
47749             this.footer = Roo.factory(this.footer, Roo);
47750         }
47751         if (this.dropTarget && this.dropTarget.xtype) {
47752             delete this.dropTarget.xtype;
47753             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47754         }
47755         
47756         
47757         this.rendered = true;
47758         this.fireEvent('render', this);
47759         return this;
47760     },
47761
47762         /**
47763          * Reconfigures the grid to use a different Store and Column Model.
47764          * The View will be bound to the new objects and refreshed.
47765          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47766          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47767          */
47768     reconfigure : function(dataSource, colModel){
47769         if(this.loadMask){
47770             this.loadMask.destroy();
47771             this.loadMask = new Roo.LoadMask(this.container,
47772                     Roo.apply({store:dataSource}, this.loadMask));
47773         }
47774         this.view.bind(dataSource, colModel);
47775         this.dataSource = dataSource;
47776         this.colModel = colModel;
47777         this.view.refresh(true);
47778     },
47779
47780     // private
47781     onKeyDown : function(e){
47782         this.fireEvent("keydown", e);
47783     },
47784
47785     /**
47786      * Destroy this grid.
47787      * @param {Boolean} removeEl True to remove the element
47788      */
47789     destroy : function(removeEl, keepListeners){
47790         if(this.loadMask){
47791             this.loadMask.destroy();
47792         }
47793         var c = this.container;
47794         c.removeAllListeners();
47795         this.view.destroy();
47796         this.colModel.purgeListeners();
47797         if(!keepListeners){
47798             this.purgeListeners();
47799         }
47800         c.update("");
47801         if(removeEl === true){
47802             c.remove();
47803         }
47804     },
47805
47806     // private
47807     processEvent : function(name, e){
47808         this.fireEvent(name, e);
47809         var t = e.getTarget();
47810         var v = this.view;
47811         var header = v.findHeaderIndex(t);
47812         if(header !== false){
47813             this.fireEvent("header" + name, this, header, e);
47814         }else{
47815             var row = v.findRowIndex(t);
47816             var cell = v.findCellIndex(t);
47817             if(row !== false){
47818                 this.fireEvent("row" + name, this, row, e);
47819                 if(cell !== false){
47820                     this.fireEvent("cell" + name, this, row, cell, e);
47821                 }
47822             }
47823         }
47824     },
47825
47826     // private
47827     onClick : function(e){
47828         this.processEvent("click", e);
47829     },
47830
47831     // private
47832     onContextMenu : function(e, t){
47833         this.processEvent("contextmenu", e);
47834     },
47835
47836     // private
47837     onDblClick : function(e){
47838         this.processEvent("dblclick", e);
47839     },
47840
47841     // private
47842     walkCells : function(row, col, step, fn, scope){
47843         var cm = this.colModel, clen = cm.getColumnCount();
47844         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47845         if(step < 0){
47846             if(col < 0){
47847                 row--;
47848                 first = false;
47849             }
47850             while(row >= 0){
47851                 if(!first){
47852                     col = clen-1;
47853                 }
47854                 first = false;
47855                 while(col >= 0){
47856                     if(fn.call(scope || this, row, col, cm) === true){
47857                         return [row, col];
47858                     }
47859                     col--;
47860                 }
47861                 row--;
47862             }
47863         } else {
47864             if(col >= clen){
47865                 row++;
47866                 first = false;
47867             }
47868             while(row < rlen){
47869                 if(!first){
47870                     col = 0;
47871                 }
47872                 first = false;
47873                 while(col < clen){
47874                     if(fn.call(scope || this, row, col, cm) === true){
47875                         return [row, col];
47876                     }
47877                     col++;
47878                 }
47879                 row++;
47880             }
47881         }
47882         return null;
47883     },
47884
47885     // private
47886     getSelections : function(){
47887         return this.selModel.getSelections();
47888     },
47889
47890     /**
47891      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47892      * but if manual update is required this method will initiate it.
47893      */
47894     autoSize : function(){
47895         if(this.rendered){
47896             this.view.layout();
47897             if(this.view.adjustForScroll){
47898                 this.view.adjustForScroll();
47899             }
47900         }
47901     },
47902
47903     /**
47904      * Returns the grid's underlying element.
47905      * @return {Element} The element
47906      */
47907     getGridEl : function(){
47908         return this.container;
47909     },
47910
47911     // private for compatibility, overridden by editor grid
47912     stopEditing : function(){},
47913
47914     /**
47915      * Returns the grid's SelectionModel.
47916      * @return {SelectionModel}
47917      */
47918     getSelectionModel : function(){
47919         if(!this.selModel){
47920             this.selModel = new Roo.grid.RowSelectionModel();
47921         }
47922         return this.selModel;
47923     },
47924
47925     /**
47926      * Returns the grid's DataSource.
47927      * @return {DataSource}
47928      */
47929     getDataSource : function(){
47930         return this.dataSource;
47931     },
47932
47933     /**
47934      * Returns the grid's ColumnModel.
47935      * @return {ColumnModel}
47936      */
47937     getColumnModel : function(){
47938         return this.colModel;
47939     },
47940
47941     /**
47942      * Returns the grid's GridView object.
47943      * @return {GridView}
47944      */
47945     getView : function(){
47946         if(!this.view){
47947             this.view = new Roo.grid.GridView(this.viewConfig);
47948         }
47949         return this.view;
47950     },
47951     /**
47952      * Called to get grid's drag proxy text, by default returns this.ddText.
47953      * @return {String}
47954      */
47955     getDragDropText : function(){
47956         var count = this.selModel.getCount();
47957         return String.format(this.ddText, count, count == 1 ? '' : 's');
47958     }
47959 });
47960 /**
47961  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47962  * %0 is replaced with the number of selected rows.
47963  * @type String
47964  */
47965 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47966  * Based on:
47967  * Ext JS Library 1.1.1
47968  * Copyright(c) 2006-2007, Ext JS, LLC.
47969  *
47970  * Originally Released Under LGPL - original licence link has changed is not relivant.
47971  *
47972  * Fork - LGPL
47973  * <script type="text/javascript">
47974  */
47975  
47976 Roo.grid.AbstractGridView = function(){
47977         this.grid = null;
47978         
47979         this.events = {
47980             "beforerowremoved" : true,
47981             "beforerowsinserted" : true,
47982             "beforerefresh" : true,
47983             "rowremoved" : true,
47984             "rowsinserted" : true,
47985             "rowupdated" : true,
47986             "refresh" : true
47987         };
47988     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47989 };
47990
47991 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47992     rowClass : "x-grid-row",
47993     cellClass : "x-grid-cell",
47994     tdClass : "x-grid-td",
47995     hdClass : "x-grid-hd",
47996     splitClass : "x-grid-hd-split",
47997     
47998         init: function(grid){
47999         this.grid = grid;
48000                 var cid = this.grid.getGridEl().id;
48001         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
48002         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
48003         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
48004         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
48005         },
48006         
48007         getColumnRenderers : function(){
48008         var renderers = [];
48009         var cm = this.grid.colModel;
48010         var colCount = cm.getColumnCount();
48011         for(var i = 0; i < colCount; i++){
48012             renderers[i] = cm.getRenderer(i);
48013         }
48014         return renderers;
48015     },
48016     
48017     getColumnIds : function(){
48018         var ids = [];
48019         var cm = this.grid.colModel;
48020         var colCount = cm.getColumnCount();
48021         for(var i = 0; i < colCount; i++){
48022             ids[i] = cm.getColumnId(i);
48023         }
48024         return ids;
48025     },
48026     
48027     getDataIndexes : function(){
48028         if(!this.indexMap){
48029             this.indexMap = this.buildIndexMap();
48030         }
48031         return this.indexMap.colToData;
48032     },
48033     
48034     getColumnIndexByDataIndex : function(dataIndex){
48035         if(!this.indexMap){
48036             this.indexMap = this.buildIndexMap();
48037         }
48038         return this.indexMap.dataToCol[dataIndex];
48039     },
48040     
48041     /**
48042      * Set a css style for a column dynamically. 
48043      * @param {Number} colIndex The index of the column
48044      * @param {String} name The css property name
48045      * @param {String} value The css value
48046      */
48047     setCSSStyle : function(colIndex, name, value){
48048         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48049         Roo.util.CSS.updateRule(selector, name, value);
48050     },
48051     
48052     generateRules : function(cm){
48053         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48054         Roo.util.CSS.removeStyleSheet(rulesId);
48055         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48056             var cid = cm.getColumnId(i);
48057             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48058                          this.tdSelector, cid, " {\n}\n",
48059                          this.hdSelector, cid, " {\n}\n",
48060                          this.splitSelector, cid, " {\n}\n");
48061         }
48062         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48063     }
48064 });/*
48065  * Based on:
48066  * Ext JS Library 1.1.1
48067  * Copyright(c) 2006-2007, Ext JS, LLC.
48068  *
48069  * Originally Released Under LGPL - original licence link has changed is not relivant.
48070  *
48071  * Fork - LGPL
48072  * <script type="text/javascript">
48073  */
48074
48075 // private
48076 // This is a support class used internally by the Grid components
48077 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48078     this.grid = grid;
48079     this.view = grid.getView();
48080     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48081     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48082     if(hd2){
48083         this.setHandleElId(Roo.id(hd));
48084         this.setOuterHandleElId(Roo.id(hd2));
48085     }
48086     this.scroll = false;
48087 };
48088 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48089     maxDragWidth: 120,
48090     getDragData : function(e){
48091         var t = Roo.lib.Event.getTarget(e);
48092         var h = this.view.findHeaderCell(t);
48093         if(h){
48094             return {ddel: h.firstChild, header:h};
48095         }
48096         return false;
48097     },
48098
48099     onInitDrag : function(e){
48100         this.view.headersDisabled = true;
48101         var clone = this.dragData.ddel.cloneNode(true);
48102         clone.id = Roo.id();
48103         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48104         this.proxy.update(clone);
48105         return true;
48106     },
48107
48108     afterValidDrop : function(){
48109         var v = this.view;
48110         setTimeout(function(){
48111             v.headersDisabled = false;
48112         }, 50);
48113     },
48114
48115     afterInvalidDrop : function(){
48116         var v = this.view;
48117         setTimeout(function(){
48118             v.headersDisabled = false;
48119         }, 50);
48120     }
48121 });
48122 /*
48123  * Based on:
48124  * Ext JS Library 1.1.1
48125  * Copyright(c) 2006-2007, Ext JS, LLC.
48126  *
48127  * Originally Released Under LGPL - original licence link has changed is not relivant.
48128  *
48129  * Fork - LGPL
48130  * <script type="text/javascript">
48131  */
48132 // private
48133 // This is a support class used internally by the Grid components
48134 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48135     this.grid = grid;
48136     this.view = grid.getView();
48137     // split the proxies so they don't interfere with mouse events
48138     this.proxyTop = Roo.DomHelper.append(document.body, {
48139         cls:"col-move-top", html:"&#160;"
48140     }, true);
48141     this.proxyBottom = Roo.DomHelper.append(document.body, {
48142         cls:"col-move-bottom", html:"&#160;"
48143     }, true);
48144     this.proxyTop.hide = this.proxyBottom.hide = function(){
48145         this.setLeftTop(-100,-100);
48146         this.setStyle("visibility", "hidden");
48147     };
48148     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48149     // temporarily disabled
48150     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48151     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48152 };
48153 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48154     proxyOffsets : [-4, -9],
48155     fly: Roo.Element.fly,
48156
48157     getTargetFromEvent : function(e){
48158         var t = Roo.lib.Event.getTarget(e);
48159         var cindex = this.view.findCellIndex(t);
48160         if(cindex !== false){
48161             return this.view.getHeaderCell(cindex);
48162         }
48163         return null;
48164     },
48165
48166     nextVisible : function(h){
48167         var v = this.view, cm = this.grid.colModel;
48168         h = h.nextSibling;
48169         while(h){
48170             if(!cm.isHidden(v.getCellIndex(h))){
48171                 return h;
48172             }
48173             h = h.nextSibling;
48174         }
48175         return null;
48176     },
48177
48178     prevVisible : function(h){
48179         var v = this.view, cm = this.grid.colModel;
48180         h = h.prevSibling;
48181         while(h){
48182             if(!cm.isHidden(v.getCellIndex(h))){
48183                 return h;
48184             }
48185             h = h.prevSibling;
48186         }
48187         return null;
48188     },
48189
48190     positionIndicator : function(h, n, e){
48191         var x = Roo.lib.Event.getPageX(e);
48192         var r = Roo.lib.Dom.getRegion(n.firstChild);
48193         var px, pt, py = r.top + this.proxyOffsets[1];
48194         if((r.right - x) <= (r.right-r.left)/2){
48195             px = r.right+this.view.borderWidth;
48196             pt = "after";
48197         }else{
48198             px = r.left;
48199             pt = "before";
48200         }
48201         var oldIndex = this.view.getCellIndex(h);
48202         var newIndex = this.view.getCellIndex(n);
48203
48204         if(this.grid.colModel.isFixed(newIndex)){
48205             return false;
48206         }
48207
48208         var locked = this.grid.colModel.isLocked(newIndex);
48209
48210         if(pt == "after"){
48211             newIndex++;
48212         }
48213         if(oldIndex < newIndex){
48214             newIndex--;
48215         }
48216         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48217             return false;
48218         }
48219         px +=  this.proxyOffsets[0];
48220         this.proxyTop.setLeftTop(px, py);
48221         this.proxyTop.show();
48222         if(!this.bottomOffset){
48223             this.bottomOffset = this.view.mainHd.getHeight();
48224         }
48225         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48226         this.proxyBottom.show();
48227         return pt;
48228     },
48229
48230     onNodeEnter : function(n, dd, e, data){
48231         if(data.header != n){
48232             this.positionIndicator(data.header, n, e);
48233         }
48234     },
48235
48236     onNodeOver : function(n, dd, e, data){
48237         var result = false;
48238         if(data.header != n){
48239             result = this.positionIndicator(data.header, n, e);
48240         }
48241         if(!result){
48242             this.proxyTop.hide();
48243             this.proxyBottom.hide();
48244         }
48245         return result ? this.dropAllowed : this.dropNotAllowed;
48246     },
48247
48248     onNodeOut : function(n, dd, e, data){
48249         this.proxyTop.hide();
48250         this.proxyBottom.hide();
48251     },
48252
48253     onNodeDrop : function(n, dd, e, data){
48254         var h = data.header;
48255         if(h != n){
48256             var cm = this.grid.colModel;
48257             var x = Roo.lib.Event.getPageX(e);
48258             var r = Roo.lib.Dom.getRegion(n.firstChild);
48259             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48260             var oldIndex = this.view.getCellIndex(h);
48261             var newIndex = this.view.getCellIndex(n);
48262             var locked = cm.isLocked(newIndex);
48263             if(pt == "after"){
48264                 newIndex++;
48265             }
48266             if(oldIndex < newIndex){
48267                 newIndex--;
48268             }
48269             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48270                 return false;
48271             }
48272             cm.setLocked(oldIndex, locked, true);
48273             cm.moveColumn(oldIndex, newIndex);
48274             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48275             return true;
48276         }
48277         return false;
48278     }
48279 });
48280 /*
48281  * Based on:
48282  * Ext JS Library 1.1.1
48283  * Copyright(c) 2006-2007, Ext JS, LLC.
48284  *
48285  * Originally Released Under LGPL - original licence link has changed is not relivant.
48286  *
48287  * Fork - LGPL
48288  * <script type="text/javascript">
48289  */
48290   
48291 /**
48292  * @class Roo.grid.GridView
48293  * @extends Roo.util.Observable
48294  *
48295  * @constructor
48296  * @param {Object} config
48297  */
48298 Roo.grid.GridView = function(config){
48299     Roo.grid.GridView.superclass.constructor.call(this);
48300     this.el = null;
48301
48302     Roo.apply(this, config);
48303 };
48304
48305 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48306
48307     /**
48308      * Override this function to apply custom css classes to rows during rendering
48309      * @param {Record} record The record
48310      * @param {Number} index
48311      * @method getRowClass
48312      */
48313     rowClass : "x-grid-row",
48314
48315     cellClass : "x-grid-col",
48316
48317     tdClass : "x-grid-td",
48318
48319     hdClass : "x-grid-hd",
48320
48321     splitClass : "x-grid-split",
48322
48323     sortClasses : ["sort-asc", "sort-desc"],
48324
48325     enableMoveAnim : false,
48326
48327     hlColor: "C3DAF9",
48328
48329     dh : Roo.DomHelper,
48330
48331     fly : Roo.Element.fly,
48332
48333     css : Roo.util.CSS,
48334
48335     borderWidth: 1,
48336
48337     splitOffset: 3,
48338
48339     scrollIncrement : 22,
48340
48341     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48342
48343     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48344
48345     bind : function(ds, cm){
48346         if(this.ds){
48347             this.ds.un("load", this.onLoad, this);
48348             this.ds.un("datachanged", this.onDataChange, this);
48349             this.ds.un("add", this.onAdd, this);
48350             this.ds.un("remove", this.onRemove, this);
48351             this.ds.un("update", this.onUpdate, this);
48352             this.ds.un("clear", this.onClear, this);
48353         }
48354         if(ds){
48355             ds.on("load", this.onLoad, this);
48356             ds.on("datachanged", this.onDataChange, this);
48357             ds.on("add", this.onAdd, this);
48358             ds.on("remove", this.onRemove, this);
48359             ds.on("update", this.onUpdate, this);
48360             ds.on("clear", this.onClear, this);
48361         }
48362         this.ds = ds;
48363
48364         if(this.cm){
48365             this.cm.un("widthchange", this.onColWidthChange, this);
48366             this.cm.un("headerchange", this.onHeaderChange, this);
48367             this.cm.un("hiddenchange", this.onHiddenChange, this);
48368             this.cm.un("columnmoved", this.onColumnMove, this);
48369             this.cm.un("columnlockchange", this.onColumnLock, this);
48370         }
48371         if(cm){
48372             this.generateRules(cm);
48373             cm.on("widthchange", this.onColWidthChange, this);
48374             cm.on("headerchange", this.onHeaderChange, this);
48375             cm.on("hiddenchange", this.onHiddenChange, this);
48376             cm.on("columnmoved", this.onColumnMove, this);
48377             cm.on("columnlockchange", this.onColumnLock, this);
48378         }
48379         this.cm = cm;
48380     },
48381
48382     init: function(grid){
48383         Roo.grid.GridView.superclass.init.call(this, grid);
48384
48385         this.bind(grid.dataSource, grid.colModel);
48386
48387         grid.on("headerclick", this.handleHeaderClick, this);
48388
48389         if(grid.trackMouseOver){
48390             grid.on("mouseover", this.onRowOver, this);
48391             grid.on("mouseout", this.onRowOut, this);
48392         }
48393         grid.cancelTextSelection = function(){};
48394         this.gridId = grid.id;
48395
48396         var tpls = this.templates || {};
48397
48398         if(!tpls.master){
48399             tpls.master = new Roo.Template(
48400                '<div class="x-grid" hidefocus="true">',
48401                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48402                   '<div class="x-grid-topbar"></div>',
48403                   '<div class="x-grid-scroller"><div></div></div>',
48404                   '<div class="x-grid-locked">',
48405                       '<div class="x-grid-header">{lockedHeader}</div>',
48406                       '<div class="x-grid-body">{lockedBody}</div>',
48407                   "</div>",
48408                   '<div class="x-grid-viewport">',
48409                       '<div class="x-grid-header">{header}</div>',
48410                       '<div class="x-grid-body">{body}</div>',
48411                   "</div>",
48412                   '<div class="x-grid-bottombar"></div>',
48413                  
48414                   '<div class="x-grid-resize-proxy">&#160;</div>',
48415                "</div>"
48416             );
48417             tpls.master.disableformats = true;
48418         }
48419
48420         if(!tpls.header){
48421             tpls.header = new Roo.Template(
48422                '<table border="0" cellspacing="0" cellpadding="0">',
48423                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48424                "</table>{splits}"
48425             );
48426             tpls.header.disableformats = true;
48427         }
48428         tpls.header.compile();
48429
48430         if(!tpls.hcell){
48431             tpls.hcell = new Roo.Template(
48432                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48433                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48434                 "</div></td>"
48435              );
48436              tpls.hcell.disableFormats = true;
48437         }
48438         tpls.hcell.compile();
48439
48440         if(!tpls.hsplit){
48441             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48442             tpls.hsplit.disableFormats = true;
48443         }
48444         tpls.hsplit.compile();
48445
48446         if(!tpls.body){
48447             tpls.body = new Roo.Template(
48448                '<table border="0" cellspacing="0" cellpadding="0">',
48449                "<tbody>{rows}</tbody>",
48450                "</table>"
48451             );
48452             tpls.body.disableFormats = true;
48453         }
48454         tpls.body.compile();
48455
48456         if(!tpls.row){
48457             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48458             tpls.row.disableFormats = true;
48459         }
48460         tpls.row.compile();
48461
48462         if(!tpls.cell){
48463             tpls.cell = new Roo.Template(
48464                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48465                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48466                 "</td>"
48467             );
48468             tpls.cell.disableFormats = true;
48469         }
48470         tpls.cell.compile();
48471
48472         this.templates = tpls;
48473     },
48474
48475     // remap these for backwards compat
48476     onColWidthChange : function(){
48477         this.updateColumns.apply(this, arguments);
48478     },
48479     onHeaderChange : function(){
48480         this.updateHeaders.apply(this, arguments);
48481     }, 
48482     onHiddenChange : function(){
48483         this.handleHiddenChange.apply(this, arguments);
48484     },
48485     onColumnMove : function(){
48486         this.handleColumnMove.apply(this, arguments);
48487     },
48488     onColumnLock : function(){
48489         this.handleLockChange.apply(this, arguments);
48490     },
48491
48492     onDataChange : function(){
48493         this.refresh();
48494         this.updateHeaderSortState();
48495     },
48496
48497     onClear : function(){
48498         this.refresh();
48499     },
48500
48501     onUpdate : function(ds, record){
48502         this.refreshRow(record);
48503     },
48504
48505     refreshRow : function(record){
48506         var ds = this.ds, index;
48507         if(typeof record == 'number'){
48508             index = record;
48509             record = ds.getAt(index);
48510         }else{
48511             index = ds.indexOf(record);
48512         }
48513         this.insertRows(ds, index, index, true);
48514         this.onRemove(ds, record, index+1, true);
48515         this.syncRowHeights(index, index);
48516         this.layout();
48517         this.fireEvent("rowupdated", this, index, record);
48518     },
48519
48520     onAdd : function(ds, records, index){
48521         this.insertRows(ds, index, index + (records.length-1));
48522     },
48523
48524     onRemove : function(ds, record, index, isUpdate){
48525         if(isUpdate !== true){
48526             this.fireEvent("beforerowremoved", this, index, record);
48527         }
48528         var bt = this.getBodyTable(), lt = this.getLockedTable();
48529         if(bt.rows[index]){
48530             bt.firstChild.removeChild(bt.rows[index]);
48531         }
48532         if(lt.rows[index]){
48533             lt.firstChild.removeChild(lt.rows[index]);
48534         }
48535         if(isUpdate !== true){
48536             this.stripeRows(index);
48537             this.syncRowHeights(index, index);
48538             this.layout();
48539             this.fireEvent("rowremoved", this, index, record);
48540         }
48541     },
48542
48543     onLoad : function(){
48544         this.scrollToTop();
48545     },
48546
48547     /**
48548      * Scrolls the grid to the top
48549      */
48550     scrollToTop : function(){
48551         if(this.scroller){
48552             this.scroller.dom.scrollTop = 0;
48553             this.syncScroll();
48554         }
48555     },
48556
48557     /**
48558      * Gets a panel in the header of the grid that can be used for toolbars etc.
48559      * After modifying the contents of this panel a call to grid.autoSize() may be
48560      * required to register any changes in size.
48561      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48562      * @return Roo.Element
48563      */
48564     getHeaderPanel : function(doShow){
48565         if(doShow){
48566             this.headerPanel.show();
48567         }
48568         return this.headerPanel;
48569     },
48570
48571     /**
48572      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48573      * After modifying the contents of this panel a call to grid.autoSize() may be
48574      * required to register any changes in size.
48575      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48576      * @return Roo.Element
48577      */
48578     getFooterPanel : function(doShow){
48579         if(doShow){
48580             this.footerPanel.show();
48581         }
48582         return this.footerPanel;
48583     },
48584
48585     initElements : function(){
48586         var E = Roo.Element;
48587         var el = this.grid.getGridEl().dom.firstChild;
48588         var cs = el.childNodes;
48589
48590         this.el = new E(el);
48591         
48592          this.focusEl = new E(el.firstChild);
48593         this.focusEl.swallowEvent("click", true);
48594         
48595         this.headerPanel = new E(cs[1]);
48596         this.headerPanel.enableDisplayMode("block");
48597
48598         this.scroller = new E(cs[2]);
48599         this.scrollSizer = new E(this.scroller.dom.firstChild);
48600
48601         this.lockedWrap = new E(cs[3]);
48602         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48603         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48604
48605         this.mainWrap = new E(cs[4]);
48606         this.mainHd = new E(this.mainWrap.dom.firstChild);
48607         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48608
48609         this.footerPanel = new E(cs[5]);
48610         this.footerPanel.enableDisplayMode("block");
48611
48612         this.resizeProxy = new E(cs[6]);
48613
48614         this.headerSelector = String.format(
48615            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48616            this.lockedHd.id, this.mainHd.id
48617         );
48618
48619         this.splitterSelector = String.format(
48620            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48621            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48622         );
48623     },
48624     idToCssName : function(s)
48625     {
48626         return s.replace(/[^a-z0-9]+/ig, '-');
48627     },
48628
48629     getHeaderCell : function(index){
48630         return Roo.DomQuery.select(this.headerSelector)[index];
48631     },
48632
48633     getHeaderCellMeasure : function(index){
48634         return this.getHeaderCell(index).firstChild;
48635     },
48636
48637     getHeaderCellText : function(index){
48638         return this.getHeaderCell(index).firstChild.firstChild;
48639     },
48640
48641     getLockedTable : function(){
48642         return this.lockedBody.dom.firstChild;
48643     },
48644
48645     getBodyTable : function(){
48646         return this.mainBody.dom.firstChild;
48647     },
48648
48649     getLockedRow : function(index){
48650         return this.getLockedTable().rows[index];
48651     },
48652
48653     getRow : function(index){
48654         return this.getBodyTable().rows[index];
48655     },
48656
48657     getRowComposite : function(index){
48658         if(!this.rowEl){
48659             this.rowEl = new Roo.CompositeElementLite();
48660         }
48661         var els = [], lrow, mrow;
48662         if(lrow = this.getLockedRow(index)){
48663             els.push(lrow);
48664         }
48665         if(mrow = this.getRow(index)){
48666             els.push(mrow);
48667         }
48668         this.rowEl.elements = els;
48669         return this.rowEl;
48670     },
48671     /**
48672      * Gets the 'td' of the cell
48673      * 
48674      * @param {Integer} rowIndex row to select
48675      * @param {Integer} colIndex column to select
48676      * 
48677      * @return {Object} 
48678      */
48679     getCell : function(rowIndex, colIndex){
48680         var locked = this.cm.getLockedCount();
48681         var source;
48682         if(colIndex < locked){
48683             source = this.lockedBody.dom.firstChild;
48684         }else{
48685             source = this.mainBody.dom.firstChild;
48686             colIndex -= locked;
48687         }
48688         return source.rows[rowIndex].childNodes[colIndex];
48689     },
48690
48691     getCellText : function(rowIndex, colIndex){
48692         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48693     },
48694
48695     getCellBox : function(cell){
48696         var b = this.fly(cell).getBox();
48697         if(Roo.isOpera){ // opera fails to report the Y
48698             b.y = cell.offsetTop + this.mainBody.getY();
48699         }
48700         return b;
48701     },
48702
48703     getCellIndex : function(cell){
48704         var id = String(cell.className).match(this.cellRE);
48705         if(id){
48706             return parseInt(id[1], 10);
48707         }
48708         return 0;
48709     },
48710
48711     findHeaderIndex : function(n){
48712         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48713         return r ? this.getCellIndex(r) : false;
48714     },
48715
48716     findHeaderCell : function(n){
48717         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48718         return r ? r : false;
48719     },
48720
48721     findRowIndex : function(n){
48722         if(!n){
48723             return false;
48724         }
48725         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48726         return r ? r.rowIndex : false;
48727     },
48728
48729     findCellIndex : function(node){
48730         var stop = this.el.dom;
48731         while(node && node != stop){
48732             if(this.findRE.test(node.className)){
48733                 return this.getCellIndex(node);
48734             }
48735             node = node.parentNode;
48736         }
48737         return false;
48738     },
48739
48740     getColumnId : function(index){
48741         return this.cm.getColumnId(index);
48742     },
48743
48744     getSplitters : function()
48745     {
48746         if(this.splitterSelector){
48747            return Roo.DomQuery.select(this.splitterSelector);
48748         }else{
48749             return null;
48750       }
48751     },
48752
48753     getSplitter : function(index){
48754         return this.getSplitters()[index];
48755     },
48756
48757     onRowOver : function(e, t){
48758         var row;
48759         if((row = this.findRowIndex(t)) !== false){
48760             this.getRowComposite(row).addClass("x-grid-row-over");
48761         }
48762     },
48763
48764     onRowOut : function(e, t){
48765         var row;
48766         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48767             this.getRowComposite(row).removeClass("x-grid-row-over");
48768         }
48769     },
48770
48771     renderHeaders : function(){
48772         var cm = this.cm;
48773         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48774         var cb = [], lb = [], sb = [], lsb = [], p = {};
48775         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48776             p.cellId = "x-grid-hd-0-" + i;
48777             p.splitId = "x-grid-csplit-0-" + i;
48778             p.id = cm.getColumnId(i);
48779             p.title = cm.getColumnTooltip(i) || "";
48780             p.value = cm.getColumnHeader(i) || "";
48781             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48782             if(!cm.isLocked(i)){
48783                 cb[cb.length] = ct.apply(p);
48784                 sb[sb.length] = st.apply(p);
48785             }else{
48786                 lb[lb.length] = ct.apply(p);
48787                 lsb[lsb.length] = st.apply(p);
48788             }
48789         }
48790         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48791                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48792     },
48793
48794     updateHeaders : function(){
48795         var html = this.renderHeaders();
48796         this.lockedHd.update(html[0]);
48797         this.mainHd.update(html[1]);
48798     },
48799
48800     /**
48801      * Focuses the specified row.
48802      * @param {Number} row The row index
48803      */
48804     focusRow : function(row)
48805     {
48806         //Roo.log('GridView.focusRow');
48807         var x = this.scroller.dom.scrollLeft;
48808         this.focusCell(row, 0, false);
48809         this.scroller.dom.scrollLeft = x;
48810     },
48811
48812     /**
48813      * Focuses the specified cell.
48814      * @param {Number} row The row index
48815      * @param {Number} col The column index
48816      * @param {Boolean} hscroll false to disable horizontal scrolling
48817      */
48818     focusCell : function(row, col, hscroll)
48819     {
48820         //Roo.log('GridView.focusCell');
48821         var el = this.ensureVisible(row, col, hscroll);
48822         this.focusEl.alignTo(el, "tl-tl");
48823         if(Roo.isGecko){
48824             this.focusEl.focus();
48825         }else{
48826             this.focusEl.focus.defer(1, this.focusEl);
48827         }
48828     },
48829
48830     /**
48831      * Scrolls the specified cell into view
48832      * @param {Number} row The row index
48833      * @param {Number} col The column index
48834      * @param {Boolean} hscroll false to disable horizontal scrolling
48835      */
48836     ensureVisible : function(row, col, hscroll)
48837     {
48838         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48839         //return null; //disable for testing.
48840         if(typeof row != "number"){
48841             row = row.rowIndex;
48842         }
48843         if(row < 0 && row >= this.ds.getCount()){
48844             return  null;
48845         }
48846         col = (col !== undefined ? col : 0);
48847         var cm = this.grid.colModel;
48848         while(cm.isHidden(col)){
48849             col++;
48850         }
48851
48852         var el = this.getCell(row, col);
48853         if(!el){
48854             return null;
48855         }
48856         var c = this.scroller.dom;
48857
48858         var ctop = parseInt(el.offsetTop, 10);
48859         var cleft = parseInt(el.offsetLeft, 10);
48860         var cbot = ctop + el.offsetHeight;
48861         var cright = cleft + el.offsetWidth;
48862         
48863         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48864         var stop = parseInt(c.scrollTop, 10);
48865         var sleft = parseInt(c.scrollLeft, 10);
48866         var sbot = stop + ch;
48867         var sright = sleft + c.clientWidth;
48868         /*
48869         Roo.log('GridView.ensureVisible:' +
48870                 ' ctop:' + ctop +
48871                 ' c.clientHeight:' + c.clientHeight +
48872                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48873                 ' stop:' + stop +
48874                 ' cbot:' + cbot +
48875                 ' sbot:' + sbot +
48876                 ' ch:' + ch  
48877                 );
48878         */
48879         if(ctop < stop){
48880              c.scrollTop = ctop;
48881             //Roo.log("set scrolltop to ctop DISABLE?");
48882         }else if(cbot > sbot){
48883             //Roo.log("set scrolltop to cbot-ch");
48884             c.scrollTop = cbot-ch;
48885         }
48886         
48887         if(hscroll !== false){
48888             if(cleft < sleft){
48889                 c.scrollLeft = cleft;
48890             }else if(cright > sright){
48891                 c.scrollLeft = cright-c.clientWidth;
48892             }
48893         }
48894          
48895         return el;
48896     },
48897
48898     updateColumns : function(){
48899         this.grid.stopEditing();
48900         var cm = this.grid.colModel, colIds = this.getColumnIds();
48901         //var totalWidth = cm.getTotalWidth();
48902         var pos = 0;
48903         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48904             //if(cm.isHidden(i)) continue;
48905             var w = cm.getColumnWidth(i);
48906             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48907             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48908         }
48909         this.updateSplitters();
48910     },
48911
48912     generateRules : function(cm){
48913         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48914         Roo.util.CSS.removeStyleSheet(rulesId);
48915         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48916             var cid = cm.getColumnId(i);
48917             var align = '';
48918             if(cm.config[i].align){
48919                 align = 'text-align:'+cm.config[i].align+';';
48920             }
48921             var hidden = '';
48922             if(cm.isHidden(i)){
48923                 hidden = 'display:none;';
48924             }
48925             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48926             ruleBuf.push(
48927                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48928                     this.hdSelector, cid, " {\n", align, width, "}\n",
48929                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48930                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48931         }
48932         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48933     },
48934
48935     updateSplitters : function(){
48936         var cm = this.cm, s = this.getSplitters();
48937         if(s){ // splitters not created yet
48938             var pos = 0, locked = true;
48939             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48940                 if(cm.isHidden(i)) continue;
48941                 var w = cm.getColumnWidth(i); // make sure it's a number
48942                 if(!cm.isLocked(i) && locked){
48943                     pos = 0;
48944                     locked = false;
48945                 }
48946                 pos += w;
48947                 s[i].style.left = (pos-this.splitOffset) + "px";
48948             }
48949         }
48950     },
48951
48952     handleHiddenChange : function(colModel, colIndex, hidden){
48953         if(hidden){
48954             this.hideColumn(colIndex);
48955         }else{
48956             this.unhideColumn(colIndex);
48957         }
48958     },
48959
48960     hideColumn : function(colIndex){
48961         var cid = this.getColumnId(colIndex);
48962         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48963         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48964         if(Roo.isSafari){
48965             this.updateHeaders();
48966         }
48967         this.updateSplitters();
48968         this.layout();
48969     },
48970
48971     unhideColumn : function(colIndex){
48972         var cid = this.getColumnId(colIndex);
48973         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48974         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48975
48976         if(Roo.isSafari){
48977             this.updateHeaders();
48978         }
48979         this.updateSplitters();
48980         this.layout();
48981     },
48982
48983     insertRows : function(dm, firstRow, lastRow, isUpdate){
48984         if(firstRow == 0 && lastRow == dm.getCount()-1){
48985             this.refresh();
48986         }else{
48987             if(!isUpdate){
48988                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48989             }
48990             var s = this.getScrollState();
48991             var markup = this.renderRows(firstRow, lastRow);
48992             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48993             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48994             this.restoreScroll(s);
48995             if(!isUpdate){
48996                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48997                 this.syncRowHeights(firstRow, lastRow);
48998                 this.stripeRows(firstRow);
48999                 this.layout();
49000             }
49001         }
49002     },
49003
49004     bufferRows : function(markup, target, index){
49005         var before = null, trows = target.rows, tbody = target.tBodies[0];
49006         if(index < trows.length){
49007             before = trows[index];
49008         }
49009         var b = document.createElement("div");
49010         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49011         var rows = b.firstChild.rows;
49012         for(var i = 0, len = rows.length; i < len; i++){
49013             if(before){
49014                 tbody.insertBefore(rows[0], before);
49015             }else{
49016                 tbody.appendChild(rows[0]);
49017             }
49018         }
49019         b.innerHTML = "";
49020         b = null;
49021     },
49022
49023     deleteRows : function(dm, firstRow, lastRow){
49024         if(dm.getRowCount()<1){
49025             this.fireEvent("beforerefresh", this);
49026             this.mainBody.update("");
49027             this.lockedBody.update("");
49028             this.fireEvent("refresh", this);
49029         }else{
49030             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49031             var bt = this.getBodyTable();
49032             var tbody = bt.firstChild;
49033             var rows = bt.rows;
49034             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49035                 tbody.removeChild(rows[firstRow]);
49036             }
49037             this.stripeRows(firstRow);
49038             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49039         }
49040     },
49041
49042     updateRows : function(dataSource, firstRow, lastRow){
49043         var s = this.getScrollState();
49044         this.refresh();
49045         this.restoreScroll(s);
49046     },
49047
49048     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49049         if(!noRefresh){
49050            this.refresh();
49051         }
49052         this.updateHeaderSortState();
49053     },
49054
49055     getScrollState : function(){
49056         
49057         var sb = this.scroller.dom;
49058         return {left: sb.scrollLeft, top: sb.scrollTop};
49059     },
49060
49061     stripeRows : function(startRow){
49062         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49063             return;
49064         }
49065         startRow = startRow || 0;
49066         var rows = this.getBodyTable().rows;
49067         var lrows = this.getLockedTable().rows;
49068         var cls = ' x-grid-row-alt ';
49069         for(var i = startRow, len = rows.length; i < len; i++){
49070             var row = rows[i], lrow = lrows[i];
49071             var isAlt = ((i+1) % 2 == 0);
49072             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49073             if(isAlt == hasAlt){
49074                 continue;
49075             }
49076             if(isAlt){
49077                 row.className += " x-grid-row-alt";
49078             }else{
49079                 row.className = row.className.replace("x-grid-row-alt", "");
49080             }
49081             if(lrow){
49082                 lrow.className = row.className;
49083             }
49084         }
49085     },
49086
49087     restoreScroll : function(state){
49088         //Roo.log('GridView.restoreScroll');
49089         var sb = this.scroller.dom;
49090         sb.scrollLeft = state.left;
49091         sb.scrollTop = state.top;
49092         this.syncScroll();
49093     },
49094
49095     syncScroll : function(){
49096         //Roo.log('GridView.syncScroll');
49097         var sb = this.scroller.dom;
49098         var sh = this.mainHd.dom;
49099         var bs = this.mainBody.dom;
49100         var lv = this.lockedBody.dom;
49101         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49102         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49103     },
49104
49105     handleScroll : function(e){
49106         this.syncScroll();
49107         var sb = this.scroller.dom;
49108         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49109         e.stopEvent();
49110     },
49111
49112     handleWheel : function(e){
49113         var d = e.getWheelDelta();
49114         this.scroller.dom.scrollTop -= d*22;
49115         // set this here to prevent jumpy scrolling on large tables
49116         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49117         e.stopEvent();
49118     },
49119
49120     renderRows : function(startRow, endRow){
49121         // pull in all the crap needed to render rows
49122         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49123         var colCount = cm.getColumnCount();
49124
49125         if(ds.getCount() < 1){
49126             return ["", ""];
49127         }
49128
49129         // build a map for all the columns
49130         var cs = [];
49131         for(var i = 0; i < colCount; i++){
49132             var name = cm.getDataIndex(i);
49133             cs[i] = {
49134                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49135                 renderer : cm.getRenderer(i),
49136                 id : cm.getColumnId(i),
49137                 locked : cm.isLocked(i)
49138             };
49139         }
49140
49141         startRow = startRow || 0;
49142         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49143
49144         // records to render
49145         var rs = ds.getRange(startRow, endRow);
49146
49147         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49148     },
49149
49150     // As much as I hate to duplicate code, this was branched because FireFox really hates
49151     // [].join("") on strings. The performance difference was substantial enough to
49152     // branch this function
49153     doRender : Roo.isGecko ?
49154             function(cs, rs, ds, startRow, colCount, stripe){
49155                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49156                 // buffers
49157                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49158                 
49159                 var hasListener = this.grid.hasListener('rowclass');
49160                 var rowcfg = {};
49161                 for(var j = 0, len = rs.length; j < len; j++){
49162                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49163                     for(var i = 0; i < colCount; i++){
49164                         c = cs[i];
49165                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49166                         p.id = c.id;
49167                         p.css = p.attr = "";
49168                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49169                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49170                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49171                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49172                         }
49173                         var markup = ct.apply(p);
49174                         if(!c.locked){
49175                             cb+= markup;
49176                         }else{
49177                             lcb+= markup;
49178                         }
49179                     }
49180                     var alt = [];
49181                     if(stripe && ((rowIndex+1) % 2 == 0)){
49182                         alt.push("x-grid-row-alt")
49183                     }
49184                     if(r.dirty){
49185                         alt.push(  " x-grid-dirty-row");
49186                     }
49187                     rp.cells = lcb;
49188                     if(this.getRowClass){
49189                         alt.push(this.getRowClass(r, rowIndex));
49190                     }
49191                     if (hasListener) {
49192                         rowcfg = {
49193                              
49194                             record: r,
49195                             rowIndex : rowIndex,
49196                             rowClass : ''
49197                         }
49198                         this.grid.fireEvent('rowclass', this, rowcfg);
49199                         alt.push(rowcfg.rowClass);
49200                     }
49201                     rp.alt = alt.join(" ");
49202                     lbuf+= rt.apply(rp);
49203                     rp.cells = cb;
49204                     buf+=  rt.apply(rp);
49205                 }
49206                 return [lbuf, buf];
49207             } :
49208             function(cs, rs, ds, startRow, colCount, stripe){
49209                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49210                 // buffers
49211                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49212                 var hasListener = this.grid.hasListener('rowclass');
49213                 var rowcfg = {};
49214                 for(var j = 0, len = rs.length; j < len; j++){
49215                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49216                     for(var i = 0; i < colCount; i++){
49217                         c = cs[i];
49218                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49219                         p.id = c.id;
49220                         p.css = p.attr = "";
49221                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49222                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49223                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49224                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49225                         }
49226                         var markup = ct.apply(p);
49227                         if(!c.locked){
49228                             cb[cb.length] = markup;
49229                         }else{
49230                             lcb[lcb.length] = markup;
49231                         }
49232                     }
49233                     var alt = [];
49234                     if(stripe && ((rowIndex+1) % 2 == 0)){
49235                         alt.push( "x-grid-row-alt");
49236                     }
49237                     if(r.dirty){
49238                         alt.push(" x-grid-dirty-row");
49239                     }
49240                     rp.cells = lcb;
49241                     if(this.getRowClass){
49242                         alt.push( this.getRowClass(r, rowIndex));
49243                     }
49244                     if (hasListener) {
49245                         rowcfg = {
49246                              
49247                             record: r,
49248                             rowIndex : rowIndex,
49249                             rowClass : ''
49250                         }
49251                         this.grid.fireEvent('rowclass', this, rowcfg);
49252                         alt.push(rowcfg.rowClass);
49253                     }
49254                     rp.alt = alt.join(" ");
49255                     rp.cells = lcb.join("");
49256                     lbuf[lbuf.length] = rt.apply(rp);
49257                     rp.cells = cb.join("");
49258                     buf[buf.length] =  rt.apply(rp);
49259                 }
49260                 return [lbuf.join(""), buf.join("")];
49261             },
49262
49263     renderBody : function(){
49264         var markup = this.renderRows();
49265         var bt = this.templates.body;
49266         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49267     },
49268
49269     /**
49270      * Refreshes the grid
49271      * @param {Boolean} headersToo
49272      */
49273     refresh : function(headersToo){
49274         this.fireEvent("beforerefresh", this);
49275         this.grid.stopEditing();
49276         var result = this.renderBody();
49277         this.lockedBody.update(result[0]);
49278         this.mainBody.update(result[1]);
49279         if(headersToo === true){
49280             this.updateHeaders();
49281             this.updateColumns();
49282             this.updateSplitters();
49283             this.updateHeaderSortState();
49284         }
49285         this.syncRowHeights();
49286         this.layout();
49287         this.fireEvent("refresh", this);
49288     },
49289
49290     handleColumnMove : function(cm, oldIndex, newIndex){
49291         this.indexMap = null;
49292         var s = this.getScrollState();
49293         this.refresh(true);
49294         this.restoreScroll(s);
49295         this.afterMove(newIndex);
49296     },
49297
49298     afterMove : function(colIndex){
49299         if(this.enableMoveAnim && Roo.enableFx){
49300             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49301         }
49302         // if multisort - fix sortOrder, and reload..
49303         if (this.grid.dataSource.multiSort) {
49304             // the we can call sort again..
49305             var dm = this.grid.dataSource;
49306             var cm = this.grid.colModel;
49307             var so = [];
49308             for(var i = 0; i < cm.config.length; i++ ) {
49309                 
49310                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49311                     continue; // dont' bother, it's not in sort list or being set.
49312                 }
49313                 
49314                 so.push(cm.config[i].dataIndex);
49315             };
49316             dm.sortOrder = so;
49317             dm.load(dm.lastOptions);
49318             
49319             
49320         }
49321         
49322     },
49323
49324     updateCell : function(dm, rowIndex, dataIndex){
49325         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49326         if(typeof colIndex == "undefined"){ // not present in grid
49327             return;
49328         }
49329         var cm = this.grid.colModel;
49330         var cell = this.getCell(rowIndex, colIndex);
49331         var cellText = this.getCellText(rowIndex, colIndex);
49332
49333         var p = {
49334             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49335             id : cm.getColumnId(colIndex),
49336             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49337         };
49338         var renderer = cm.getRenderer(colIndex);
49339         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49340         if(typeof val == "undefined" || val === "") val = "&#160;";
49341         cellText.innerHTML = val;
49342         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49343         this.syncRowHeights(rowIndex, rowIndex);
49344     },
49345
49346     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49347         var maxWidth = 0;
49348         if(this.grid.autoSizeHeaders){
49349             var h = this.getHeaderCellMeasure(colIndex);
49350             maxWidth = Math.max(maxWidth, h.scrollWidth);
49351         }
49352         var tb, index;
49353         if(this.cm.isLocked(colIndex)){
49354             tb = this.getLockedTable();
49355             index = colIndex;
49356         }else{
49357             tb = this.getBodyTable();
49358             index = colIndex - this.cm.getLockedCount();
49359         }
49360         if(tb && tb.rows){
49361             var rows = tb.rows;
49362             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49363             for(var i = 0; i < stopIndex; i++){
49364                 var cell = rows[i].childNodes[index].firstChild;
49365                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49366             }
49367         }
49368         return maxWidth + /*margin for error in IE*/ 5;
49369     },
49370     /**
49371      * Autofit a column to its content.
49372      * @param {Number} colIndex
49373      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49374      */
49375      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49376          if(this.cm.isHidden(colIndex)){
49377              return; // can't calc a hidden column
49378          }
49379         if(forceMinSize){
49380             var cid = this.cm.getColumnId(colIndex);
49381             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49382            if(this.grid.autoSizeHeaders){
49383                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49384            }
49385         }
49386         var newWidth = this.calcColumnWidth(colIndex);
49387         this.cm.setColumnWidth(colIndex,
49388             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49389         if(!suppressEvent){
49390             this.grid.fireEvent("columnresize", colIndex, newWidth);
49391         }
49392     },
49393
49394     /**
49395      * Autofits all columns to their content and then expands to fit any extra space in the grid
49396      */
49397      autoSizeColumns : function(){
49398         var cm = this.grid.colModel;
49399         var colCount = cm.getColumnCount();
49400         for(var i = 0; i < colCount; i++){
49401             this.autoSizeColumn(i, true, true);
49402         }
49403         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49404             this.fitColumns();
49405         }else{
49406             this.updateColumns();
49407             this.layout();
49408         }
49409     },
49410
49411     /**
49412      * Autofits all columns to the grid's width proportionate with their current size
49413      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49414      */
49415     fitColumns : function(reserveScrollSpace){
49416         var cm = this.grid.colModel;
49417         var colCount = cm.getColumnCount();
49418         var cols = [];
49419         var width = 0;
49420         var i, w;
49421         for (i = 0; i < colCount; i++){
49422             if(!cm.isHidden(i) && !cm.isFixed(i)){
49423                 w = cm.getColumnWidth(i);
49424                 cols.push(i);
49425                 cols.push(w);
49426                 width += w;
49427             }
49428         }
49429         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49430         if(reserveScrollSpace){
49431             avail -= 17;
49432         }
49433         var frac = (avail - cm.getTotalWidth())/width;
49434         while (cols.length){
49435             w = cols.pop();
49436             i = cols.pop();
49437             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49438         }
49439         this.updateColumns();
49440         this.layout();
49441     },
49442
49443     onRowSelect : function(rowIndex){
49444         var row = this.getRowComposite(rowIndex);
49445         row.addClass("x-grid-row-selected");
49446     },
49447
49448     onRowDeselect : function(rowIndex){
49449         var row = this.getRowComposite(rowIndex);
49450         row.removeClass("x-grid-row-selected");
49451     },
49452
49453     onCellSelect : function(row, col){
49454         var cell = this.getCell(row, col);
49455         if(cell){
49456             Roo.fly(cell).addClass("x-grid-cell-selected");
49457         }
49458     },
49459
49460     onCellDeselect : function(row, col){
49461         var cell = this.getCell(row, col);
49462         if(cell){
49463             Roo.fly(cell).removeClass("x-grid-cell-selected");
49464         }
49465     },
49466
49467     updateHeaderSortState : function(){
49468         
49469         // sort state can be single { field: xxx, direction : yyy}
49470         // or   { xxx=>ASC , yyy : DESC ..... }
49471         
49472         var mstate = {};
49473         if (!this.ds.multiSort) { 
49474             var state = this.ds.getSortState();
49475             if(!state){
49476                 return;
49477             }
49478             mstate[state.field] = state.direction;
49479             // FIXME... - this is not used here.. but might be elsewhere..
49480             this.sortState = state;
49481             
49482         } else {
49483             mstate = this.ds.sortToggle;
49484         }
49485         //remove existing sort classes..
49486         
49487         var sc = this.sortClasses;
49488         var hds = this.el.select(this.headerSelector).removeClass(sc);
49489         
49490         for(var f in mstate) {
49491         
49492             var sortColumn = this.cm.findColumnIndex(f);
49493             
49494             if(sortColumn != -1){
49495                 var sortDir = mstate[f];        
49496                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49497             }
49498         }
49499         
49500          
49501         
49502     },
49503
49504
49505     handleHeaderClick : function(g, index){
49506         if(this.headersDisabled){
49507             return;
49508         }
49509         var dm = g.dataSource, cm = g.colModel;
49510         if(!cm.isSortable(index)){
49511             return;
49512         }
49513         g.stopEditing();
49514         
49515         if (dm.multiSort) {
49516             // update the sortOrder
49517             var so = [];
49518             for(var i = 0; i < cm.config.length; i++ ) {
49519                 
49520                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49521                     continue; // dont' bother, it's not in sort list or being set.
49522                 }
49523                 
49524                 so.push(cm.config[i].dataIndex);
49525             };
49526             dm.sortOrder = so;
49527         }
49528         
49529         
49530         dm.sort(cm.getDataIndex(index));
49531     },
49532
49533
49534     destroy : function(){
49535         if(this.colMenu){
49536             this.colMenu.removeAll();
49537             Roo.menu.MenuMgr.unregister(this.colMenu);
49538             this.colMenu.getEl().remove();
49539             delete this.colMenu;
49540         }
49541         if(this.hmenu){
49542             this.hmenu.removeAll();
49543             Roo.menu.MenuMgr.unregister(this.hmenu);
49544             this.hmenu.getEl().remove();
49545             delete this.hmenu;
49546         }
49547         if(this.grid.enableColumnMove){
49548             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49549             if(dds){
49550                 for(var dd in dds){
49551                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49552                         var elid = dds[dd].dragElId;
49553                         dds[dd].unreg();
49554                         Roo.get(elid).remove();
49555                     } else if(dds[dd].config.isTarget){
49556                         dds[dd].proxyTop.remove();
49557                         dds[dd].proxyBottom.remove();
49558                         dds[dd].unreg();
49559                     }
49560                     if(Roo.dd.DDM.locationCache[dd]){
49561                         delete Roo.dd.DDM.locationCache[dd];
49562                     }
49563                 }
49564                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49565             }
49566         }
49567         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49568         this.bind(null, null);
49569         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49570     },
49571
49572     handleLockChange : function(){
49573         this.refresh(true);
49574     },
49575
49576     onDenyColumnLock : function(){
49577
49578     },
49579
49580     onDenyColumnHide : function(){
49581
49582     },
49583
49584     handleHdMenuClick : function(item){
49585         var index = this.hdCtxIndex;
49586         var cm = this.cm, ds = this.ds;
49587         switch(item.id){
49588             case "asc":
49589                 ds.sort(cm.getDataIndex(index), "ASC");
49590                 break;
49591             case "desc":
49592                 ds.sort(cm.getDataIndex(index), "DESC");
49593                 break;
49594             case "lock":
49595                 var lc = cm.getLockedCount();
49596                 if(cm.getColumnCount(true) <= lc+1){
49597                     this.onDenyColumnLock();
49598                     return;
49599                 }
49600                 if(lc != index){
49601                     cm.setLocked(index, true, true);
49602                     cm.moveColumn(index, lc);
49603                     this.grid.fireEvent("columnmove", index, lc);
49604                 }else{
49605                     cm.setLocked(index, true);
49606                 }
49607             break;
49608             case "unlock":
49609                 var lc = cm.getLockedCount();
49610                 if((lc-1) != index){
49611                     cm.setLocked(index, false, true);
49612                     cm.moveColumn(index, lc-1);
49613                     this.grid.fireEvent("columnmove", index, lc-1);
49614                 }else{
49615                     cm.setLocked(index, false);
49616                 }
49617             break;
49618             default:
49619                 index = cm.getIndexById(item.id.substr(4));
49620                 if(index != -1){
49621                     if(item.checked && cm.getColumnCount(true) <= 1){
49622                         this.onDenyColumnHide();
49623                         return false;
49624                     }
49625                     cm.setHidden(index, item.checked);
49626                 }
49627         }
49628         return true;
49629     },
49630
49631     beforeColMenuShow : function(){
49632         var cm = this.cm,  colCount = cm.getColumnCount();
49633         this.colMenu.removeAll();
49634         for(var i = 0; i < colCount; i++){
49635             this.colMenu.add(new Roo.menu.CheckItem({
49636                 id: "col-"+cm.getColumnId(i),
49637                 text: cm.getColumnHeader(i),
49638                 checked: !cm.isHidden(i),
49639                 hideOnClick:false
49640             }));
49641         }
49642     },
49643
49644     handleHdCtx : function(g, index, e){
49645         e.stopEvent();
49646         var hd = this.getHeaderCell(index);
49647         this.hdCtxIndex = index;
49648         var ms = this.hmenu.items, cm = this.cm;
49649         ms.get("asc").setDisabled(!cm.isSortable(index));
49650         ms.get("desc").setDisabled(!cm.isSortable(index));
49651         if(this.grid.enableColLock !== false){
49652             ms.get("lock").setDisabled(cm.isLocked(index));
49653             ms.get("unlock").setDisabled(!cm.isLocked(index));
49654         }
49655         this.hmenu.show(hd, "tl-bl");
49656     },
49657
49658     handleHdOver : function(e){
49659         var hd = this.findHeaderCell(e.getTarget());
49660         if(hd && !this.headersDisabled){
49661             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49662                this.fly(hd).addClass("x-grid-hd-over");
49663             }
49664         }
49665     },
49666
49667     handleHdOut : function(e){
49668         var hd = this.findHeaderCell(e.getTarget());
49669         if(hd){
49670             this.fly(hd).removeClass("x-grid-hd-over");
49671         }
49672     },
49673
49674     handleSplitDblClick : function(e, t){
49675         var i = this.getCellIndex(t);
49676         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49677             this.autoSizeColumn(i, true);
49678             this.layout();
49679         }
49680     },
49681
49682     render : function(){
49683
49684         var cm = this.cm;
49685         var colCount = cm.getColumnCount();
49686
49687         if(this.grid.monitorWindowResize === true){
49688             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49689         }
49690         var header = this.renderHeaders();
49691         var body = this.templates.body.apply({rows:""});
49692         var html = this.templates.master.apply({
49693             lockedBody: body,
49694             body: body,
49695             lockedHeader: header[0],
49696             header: header[1]
49697         });
49698
49699         //this.updateColumns();
49700
49701         this.grid.getGridEl().dom.innerHTML = html;
49702
49703         this.initElements();
49704         
49705         // a kludge to fix the random scolling effect in webkit
49706         this.el.on("scroll", function() {
49707             this.el.dom.scrollTop=0; // hopefully not recursive..
49708         },this);
49709
49710         this.scroller.on("scroll", this.handleScroll, this);
49711         this.lockedBody.on("mousewheel", this.handleWheel, this);
49712         this.mainBody.on("mousewheel", this.handleWheel, this);
49713
49714         this.mainHd.on("mouseover", this.handleHdOver, this);
49715         this.mainHd.on("mouseout", this.handleHdOut, this);
49716         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49717                 {delegate: "."+this.splitClass});
49718
49719         this.lockedHd.on("mouseover", this.handleHdOver, this);
49720         this.lockedHd.on("mouseout", this.handleHdOut, this);
49721         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49722                 {delegate: "."+this.splitClass});
49723
49724         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49725             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49726         }
49727
49728         this.updateSplitters();
49729
49730         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49731             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49732             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49733         }
49734
49735         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49736             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49737             this.hmenu.add(
49738                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49739                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49740             );
49741             if(this.grid.enableColLock !== false){
49742                 this.hmenu.add('-',
49743                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49744                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49745                 );
49746             }
49747             if(this.grid.enableColumnHide !== false){
49748
49749                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49750                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49751                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49752
49753                 this.hmenu.add('-',
49754                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49755                 );
49756             }
49757             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49758
49759             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49760         }
49761
49762         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49763             this.dd = new Roo.grid.GridDragZone(this.grid, {
49764                 ddGroup : this.grid.ddGroup || 'GridDD'
49765             });
49766         }
49767
49768         /*
49769         for(var i = 0; i < colCount; i++){
49770             if(cm.isHidden(i)){
49771                 this.hideColumn(i);
49772             }
49773             if(cm.config[i].align){
49774                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49775                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49776             }
49777         }*/
49778         
49779         this.updateHeaderSortState();
49780
49781         this.beforeInitialResize();
49782         this.layout(true);
49783
49784         // two part rendering gives faster view to the user
49785         this.renderPhase2.defer(1, this);
49786     },
49787
49788     renderPhase2 : function(){
49789         // render the rows now
49790         this.refresh();
49791         if(this.grid.autoSizeColumns){
49792             this.autoSizeColumns();
49793         }
49794     },
49795
49796     beforeInitialResize : function(){
49797
49798     },
49799
49800     onColumnSplitterMoved : function(i, w){
49801         this.userResized = true;
49802         var cm = this.grid.colModel;
49803         cm.setColumnWidth(i, w, true);
49804         var cid = cm.getColumnId(i);
49805         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49806         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49807         this.updateSplitters();
49808         this.layout();
49809         this.grid.fireEvent("columnresize", i, w);
49810     },
49811
49812     syncRowHeights : function(startIndex, endIndex){
49813         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49814             startIndex = startIndex || 0;
49815             var mrows = this.getBodyTable().rows;
49816             var lrows = this.getLockedTable().rows;
49817             var len = mrows.length-1;
49818             endIndex = Math.min(endIndex || len, len);
49819             for(var i = startIndex; i <= endIndex; i++){
49820                 var m = mrows[i], l = lrows[i];
49821                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49822                 m.style.height = l.style.height = h + "px";
49823             }
49824         }
49825     },
49826
49827     layout : function(initialRender, is2ndPass){
49828         var g = this.grid;
49829         var auto = g.autoHeight;
49830         var scrollOffset = 16;
49831         var c = g.getGridEl(), cm = this.cm,
49832                 expandCol = g.autoExpandColumn,
49833                 gv = this;
49834         //c.beginMeasure();
49835
49836         if(!c.dom.offsetWidth){ // display:none?
49837             if(initialRender){
49838                 this.lockedWrap.show();
49839                 this.mainWrap.show();
49840             }
49841             return;
49842         }
49843
49844         var hasLock = this.cm.isLocked(0);
49845
49846         var tbh = this.headerPanel.getHeight();
49847         var bbh = this.footerPanel.getHeight();
49848
49849         if(auto){
49850             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49851             var newHeight = ch + c.getBorderWidth("tb");
49852             if(g.maxHeight){
49853                 newHeight = Math.min(g.maxHeight, newHeight);
49854             }
49855             c.setHeight(newHeight);
49856         }
49857
49858         if(g.autoWidth){
49859             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49860         }
49861
49862         var s = this.scroller;
49863
49864         var csize = c.getSize(true);
49865
49866         this.el.setSize(csize.width, csize.height);
49867
49868         this.headerPanel.setWidth(csize.width);
49869         this.footerPanel.setWidth(csize.width);
49870
49871         var hdHeight = this.mainHd.getHeight();
49872         var vw = csize.width;
49873         var vh = csize.height - (tbh + bbh);
49874
49875         s.setSize(vw, vh);
49876
49877         var bt = this.getBodyTable();
49878         var ltWidth = hasLock ?
49879                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49880
49881         var scrollHeight = bt.offsetHeight;
49882         var scrollWidth = ltWidth + bt.offsetWidth;
49883         var vscroll = false, hscroll = false;
49884
49885         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49886
49887         var lw = this.lockedWrap, mw = this.mainWrap;
49888         var lb = this.lockedBody, mb = this.mainBody;
49889
49890         setTimeout(function(){
49891             var t = s.dom.offsetTop;
49892             var w = s.dom.clientWidth,
49893                 h = s.dom.clientHeight;
49894
49895             lw.setTop(t);
49896             lw.setSize(ltWidth, h);
49897
49898             mw.setLeftTop(ltWidth, t);
49899             mw.setSize(w-ltWidth, h);
49900
49901             lb.setHeight(h-hdHeight);
49902             mb.setHeight(h-hdHeight);
49903
49904             if(is2ndPass !== true && !gv.userResized && expandCol){
49905                 // high speed resize without full column calculation
49906                 
49907                 var ci = cm.getIndexById(expandCol);
49908                 if (ci < 0) {
49909                     ci = cm.findColumnIndex(expandCol);
49910                 }
49911                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49912                 var expandId = cm.getColumnId(ci);
49913                 var  tw = cm.getTotalWidth(false);
49914                 var currentWidth = cm.getColumnWidth(ci);
49915                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49916                 if(currentWidth != cw){
49917                     cm.setColumnWidth(ci, cw, true);
49918                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49919                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49920                     gv.updateSplitters();
49921                     gv.layout(false, true);
49922                 }
49923             }
49924
49925             if(initialRender){
49926                 lw.show();
49927                 mw.show();
49928             }
49929             //c.endMeasure();
49930         }, 10);
49931     },
49932
49933     onWindowResize : function(){
49934         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49935             return;
49936         }
49937         this.layout();
49938     },
49939
49940     appendFooter : function(parentEl){
49941         return null;
49942     },
49943
49944     sortAscText : "Sort Ascending",
49945     sortDescText : "Sort Descending",
49946     lockText : "Lock Column",
49947     unlockText : "Unlock Column",
49948     columnsText : "Columns"
49949 });
49950
49951
49952 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49953     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49954     this.proxy.el.addClass('x-grid3-col-dd');
49955 };
49956
49957 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49958     handleMouseDown : function(e){
49959
49960     },
49961
49962     callHandleMouseDown : function(e){
49963         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49964     }
49965 });
49966 /*
49967  * Based on:
49968  * Ext JS Library 1.1.1
49969  * Copyright(c) 2006-2007, Ext JS, LLC.
49970  *
49971  * Originally Released Under LGPL - original licence link has changed is not relivant.
49972  *
49973  * Fork - LGPL
49974  * <script type="text/javascript">
49975  */
49976  
49977 // private
49978 // This is a support class used internally by the Grid components
49979 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49980     this.grid = grid;
49981     this.view = grid.getView();
49982     this.proxy = this.view.resizeProxy;
49983     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49984         "gridSplitters" + this.grid.getGridEl().id, {
49985         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49986     });
49987     this.setHandleElId(Roo.id(hd));
49988     this.setOuterHandleElId(Roo.id(hd2));
49989     this.scroll = false;
49990 };
49991 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49992     fly: Roo.Element.fly,
49993
49994     b4StartDrag : function(x, y){
49995         this.view.headersDisabled = true;
49996         this.proxy.setHeight(this.view.mainWrap.getHeight());
49997         var w = this.cm.getColumnWidth(this.cellIndex);
49998         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49999         this.resetConstraints();
50000         this.setXConstraint(minw, 1000);
50001         this.setYConstraint(0, 0);
50002         this.minX = x - minw;
50003         this.maxX = x + 1000;
50004         this.startPos = x;
50005         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
50006     },
50007
50008
50009     handleMouseDown : function(e){
50010         ev = Roo.EventObject.setEvent(e);
50011         var t = this.fly(ev.getTarget());
50012         if(t.hasClass("x-grid-split")){
50013             this.cellIndex = this.view.getCellIndex(t.dom);
50014             this.split = t.dom;
50015             this.cm = this.grid.colModel;
50016             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50017                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50018             }
50019         }
50020     },
50021
50022     endDrag : function(e){
50023         this.view.headersDisabled = false;
50024         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50025         var diff = endX - this.startPos;
50026         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50027     },
50028
50029     autoOffset : function(){
50030         this.setDelta(0,0);
50031     }
50032 });/*
50033  * Based on:
50034  * Ext JS Library 1.1.1
50035  * Copyright(c) 2006-2007, Ext JS, LLC.
50036  *
50037  * Originally Released Under LGPL - original licence link has changed is not relivant.
50038  *
50039  * Fork - LGPL
50040  * <script type="text/javascript">
50041  */
50042  
50043 // private
50044 // This is a support class used internally by the Grid components
50045 Roo.grid.GridDragZone = function(grid, config){
50046     this.view = grid.getView();
50047     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50048     if(this.view.lockedBody){
50049         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50050         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50051     }
50052     this.scroll = false;
50053     this.grid = grid;
50054     this.ddel = document.createElement('div');
50055     this.ddel.className = 'x-grid-dd-wrap';
50056 };
50057
50058 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50059     ddGroup : "GridDD",
50060
50061     getDragData : function(e){
50062         var t = Roo.lib.Event.getTarget(e);
50063         var rowIndex = this.view.findRowIndex(t);
50064         if(rowIndex !== false){
50065             var sm = this.grid.selModel;
50066             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50067               //  sm.mouseDown(e, t);
50068             //}
50069             if (e.hasModifier()){
50070                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50071             }
50072             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50073         }
50074         return false;
50075     },
50076
50077     onInitDrag : function(e){
50078         var data = this.dragData;
50079         this.ddel.innerHTML = this.grid.getDragDropText();
50080         this.proxy.update(this.ddel);
50081         // fire start drag?
50082     },
50083
50084     afterRepair : function(){
50085         this.dragging = false;
50086     },
50087
50088     getRepairXY : function(e, data){
50089         return false;
50090     },
50091
50092     onEndDrag : function(data, e){
50093         // fire end drag?
50094     },
50095
50096     onValidDrop : function(dd, e, id){
50097         // fire drag drop?
50098         this.hideProxy();
50099     },
50100
50101     beforeInvalidDrop : function(e, id){
50102
50103     }
50104 });/*
50105  * Based on:
50106  * Ext JS Library 1.1.1
50107  * Copyright(c) 2006-2007, Ext JS, LLC.
50108  *
50109  * Originally Released Under LGPL - original licence link has changed is not relivant.
50110  *
50111  * Fork - LGPL
50112  * <script type="text/javascript">
50113  */
50114  
50115
50116 /**
50117  * @class Roo.grid.ColumnModel
50118  * @extends Roo.util.Observable
50119  * This is the default implementation of a ColumnModel used by the Grid. It defines
50120  * the columns in the grid.
50121  * <br>Usage:<br>
50122  <pre><code>
50123  var colModel = new Roo.grid.ColumnModel([
50124         {header: "Ticker", width: 60, sortable: true, locked: true},
50125         {header: "Company Name", width: 150, sortable: true},
50126         {header: "Market Cap.", width: 100, sortable: true},
50127         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50128         {header: "Employees", width: 100, sortable: true, resizable: false}
50129  ]);
50130  </code></pre>
50131  * <p>
50132  
50133  * The config options listed for this class are options which may appear in each
50134  * individual column definition.
50135  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50136  * @constructor
50137  * @param {Object} config An Array of column config objects. See this class's
50138  * config objects for details.
50139 */
50140 Roo.grid.ColumnModel = function(config){
50141         /**
50142      * The config passed into the constructor
50143      */
50144     this.config = config;
50145     this.lookup = {};
50146
50147     // if no id, create one
50148     // if the column does not have a dataIndex mapping,
50149     // map it to the order it is in the config
50150     for(var i = 0, len = config.length; i < len; i++){
50151         var c = config[i];
50152         if(typeof c.dataIndex == "undefined"){
50153             c.dataIndex = i;
50154         }
50155         if(typeof c.renderer == "string"){
50156             c.renderer = Roo.util.Format[c.renderer];
50157         }
50158         if(typeof c.id == "undefined"){
50159             c.id = Roo.id();
50160         }
50161         if(c.editor && c.editor.xtype){
50162             c.editor  = Roo.factory(c.editor, Roo.grid);
50163         }
50164         if(c.editor && c.editor.isFormField){
50165             c.editor = new Roo.grid.GridEditor(c.editor);
50166         }
50167         this.lookup[c.id] = c;
50168     }
50169
50170     /**
50171      * The width of columns which have no width specified (defaults to 100)
50172      * @type Number
50173      */
50174     this.defaultWidth = 100;
50175
50176     /**
50177      * Default sortable of columns which have no sortable specified (defaults to false)
50178      * @type Boolean
50179      */
50180     this.defaultSortable = false;
50181
50182     this.addEvents({
50183         /**
50184              * @event widthchange
50185              * Fires when the width of a column changes.
50186              * @param {ColumnModel} this
50187              * @param {Number} columnIndex The column index
50188              * @param {Number} newWidth The new width
50189              */
50190             "widthchange": true,
50191         /**
50192              * @event headerchange
50193              * Fires when the text of a header changes.
50194              * @param {ColumnModel} this
50195              * @param {Number} columnIndex The column index
50196              * @param {Number} newText The new header text
50197              */
50198             "headerchange": true,
50199         /**
50200              * @event hiddenchange
50201              * Fires when a column is hidden or "unhidden".
50202              * @param {ColumnModel} this
50203              * @param {Number} columnIndex The column index
50204              * @param {Boolean} hidden true if hidden, false otherwise
50205              */
50206             "hiddenchange": true,
50207             /**
50208          * @event columnmoved
50209          * Fires when a column is moved.
50210          * @param {ColumnModel} this
50211          * @param {Number} oldIndex
50212          * @param {Number} newIndex
50213          */
50214         "columnmoved" : true,
50215         /**
50216          * @event columlockchange
50217          * Fires when a column's locked state is changed
50218          * @param {ColumnModel} this
50219          * @param {Number} colIndex
50220          * @param {Boolean} locked true if locked
50221          */
50222         "columnlockchange" : true
50223     });
50224     Roo.grid.ColumnModel.superclass.constructor.call(this);
50225 };
50226 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50227     /**
50228      * @cfg {String} header The header text to display in the Grid view.
50229      */
50230     /**
50231      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50232      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50233      * specified, the column's index is used as an index into the Record's data Array.
50234      */
50235     /**
50236      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50237      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50238      */
50239     /**
50240      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50241      * Defaults to the value of the {@link #defaultSortable} property.
50242      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50243      */
50244     /**
50245      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50246      */
50247     /**
50248      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50249      */
50250     /**
50251      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50252      */
50253     /**
50254      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50255      */
50256     /**
50257      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50258      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50259      * default renderer uses the raw data value.
50260      */
50261        /**
50262      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50263      */
50264     /**
50265      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50266      */
50267
50268     /**
50269      * Returns the id of the column at the specified index.
50270      * @param {Number} index The column index
50271      * @return {String} the id
50272      */
50273     getColumnId : function(index){
50274         return this.config[index].id;
50275     },
50276
50277     /**
50278      * Returns the column for a specified id.
50279      * @param {String} id The column id
50280      * @return {Object} the column
50281      */
50282     getColumnById : function(id){
50283         return this.lookup[id];
50284     },
50285
50286     
50287     /**
50288      * Returns the column for a specified dataIndex.
50289      * @param {String} dataIndex The column dataIndex
50290      * @return {Object|Boolean} the column or false if not found
50291      */
50292     getColumnByDataIndex: function(dataIndex){
50293         var index = this.findColumnIndex(dataIndex);
50294         return index > -1 ? this.config[index] : false;
50295     },
50296     
50297     /**
50298      * Returns the index for a specified column id.
50299      * @param {String} id The column id
50300      * @return {Number} the index, or -1 if not found
50301      */
50302     getIndexById : function(id){
50303         for(var i = 0, len = this.config.length; i < len; i++){
50304             if(this.config[i].id == id){
50305                 return i;
50306             }
50307         }
50308         return -1;
50309     },
50310     
50311     /**
50312      * Returns the index for a specified column dataIndex.
50313      * @param {String} dataIndex The column dataIndex
50314      * @return {Number} the index, or -1 if not found
50315      */
50316     
50317     findColumnIndex : function(dataIndex){
50318         for(var i = 0, len = this.config.length; i < len; i++){
50319             if(this.config[i].dataIndex == dataIndex){
50320                 return i;
50321             }
50322         }
50323         return -1;
50324     },
50325     
50326     
50327     moveColumn : function(oldIndex, newIndex){
50328         var c = this.config[oldIndex];
50329         this.config.splice(oldIndex, 1);
50330         this.config.splice(newIndex, 0, c);
50331         this.dataMap = null;
50332         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50333     },
50334
50335     isLocked : function(colIndex){
50336         return this.config[colIndex].locked === true;
50337     },
50338
50339     setLocked : function(colIndex, value, suppressEvent){
50340         if(this.isLocked(colIndex) == value){
50341             return;
50342         }
50343         this.config[colIndex].locked = value;
50344         if(!suppressEvent){
50345             this.fireEvent("columnlockchange", this, colIndex, value);
50346         }
50347     },
50348
50349     getTotalLockedWidth : function(){
50350         var totalWidth = 0;
50351         for(var i = 0; i < this.config.length; i++){
50352             if(this.isLocked(i) && !this.isHidden(i)){
50353                 this.totalWidth += this.getColumnWidth(i);
50354             }
50355         }
50356         return totalWidth;
50357     },
50358
50359     getLockedCount : function(){
50360         for(var i = 0, len = this.config.length; i < len; i++){
50361             if(!this.isLocked(i)){
50362                 return i;
50363             }
50364         }
50365     },
50366
50367     /**
50368      * Returns the number of columns.
50369      * @return {Number}
50370      */
50371     getColumnCount : function(visibleOnly){
50372         if(visibleOnly === true){
50373             var c = 0;
50374             for(var i = 0, len = this.config.length; i < len; i++){
50375                 if(!this.isHidden(i)){
50376                     c++;
50377                 }
50378             }
50379             return c;
50380         }
50381         return this.config.length;
50382     },
50383
50384     /**
50385      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50386      * @param {Function} fn
50387      * @param {Object} scope (optional)
50388      * @return {Array} result
50389      */
50390     getColumnsBy : function(fn, scope){
50391         var r = [];
50392         for(var i = 0, len = this.config.length; i < len; i++){
50393             var c = this.config[i];
50394             if(fn.call(scope||this, c, i) === true){
50395                 r[r.length] = c;
50396             }
50397         }
50398         return r;
50399     },
50400
50401     /**
50402      * Returns true if the specified column is sortable.
50403      * @param {Number} col The column index
50404      * @return {Boolean}
50405      */
50406     isSortable : function(col){
50407         if(typeof this.config[col].sortable == "undefined"){
50408             return this.defaultSortable;
50409         }
50410         return this.config[col].sortable;
50411     },
50412
50413     /**
50414      * Returns the rendering (formatting) function defined for the column.
50415      * @param {Number} col The column index.
50416      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50417      */
50418     getRenderer : function(col){
50419         if(!this.config[col].renderer){
50420             return Roo.grid.ColumnModel.defaultRenderer;
50421         }
50422         return this.config[col].renderer;
50423     },
50424
50425     /**
50426      * Sets the rendering (formatting) function for a column.
50427      * @param {Number} col The column index
50428      * @param {Function} fn The function to use to process the cell's raw data
50429      * to return HTML markup for the grid view. The render function is called with
50430      * the following parameters:<ul>
50431      * <li>Data value.</li>
50432      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50433      * <li>css A CSS style string to apply to the table cell.</li>
50434      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50435      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50436      * <li>Row index</li>
50437      * <li>Column index</li>
50438      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50439      */
50440     setRenderer : function(col, fn){
50441         this.config[col].renderer = fn;
50442     },
50443
50444     /**
50445      * Returns the width for the specified column.
50446      * @param {Number} col The column index
50447      * @return {Number}
50448      */
50449     getColumnWidth : function(col){
50450         return this.config[col].width * 1 || this.defaultWidth;
50451     },
50452
50453     /**
50454      * Sets the width for a column.
50455      * @param {Number} col The column index
50456      * @param {Number} width The new width
50457      */
50458     setColumnWidth : function(col, width, suppressEvent){
50459         this.config[col].width = width;
50460         this.totalWidth = null;
50461         if(!suppressEvent){
50462              this.fireEvent("widthchange", this, col, width);
50463         }
50464     },
50465
50466     /**
50467      * Returns the total width of all columns.
50468      * @param {Boolean} includeHidden True to include hidden column widths
50469      * @return {Number}
50470      */
50471     getTotalWidth : function(includeHidden){
50472         if(!this.totalWidth){
50473             this.totalWidth = 0;
50474             for(var i = 0, len = this.config.length; i < len; i++){
50475                 if(includeHidden || !this.isHidden(i)){
50476                     this.totalWidth += this.getColumnWidth(i);
50477                 }
50478             }
50479         }
50480         return this.totalWidth;
50481     },
50482
50483     /**
50484      * Returns the header for the specified column.
50485      * @param {Number} col The column index
50486      * @return {String}
50487      */
50488     getColumnHeader : function(col){
50489         return this.config[col].header;
50490     },
50491
50492     /**
50493      * Sets the header for a column.
50494      * @param {Number} col The column index
50495      * @param {String} header The new header
50496      */
50497     setColumnHeader : function(col, header){
50498         this.config[col].header = header;
50499         this.fireEvent("headerchange", this, col, header);
50500     },
50501
50502     /**
50503      * Returns the tooltip for the specified column.
50504      * @param {Number} col The column index
50505      * @return {String}
50506      */
50507     getColumnTooltip : function(col){
50508             return this.config[col].tooltip;
50509     },
50510     /**
50511      * Sets the tooltip for a column.
50512      * @param {Number} col The column index
50513      * @param {String} tooltip The new tooltip
50514      */
50515     setColumnTooltip : function(col, tooltip){
50516             this.config[col].tooltip = tooltip;
50517     },
50518
50519     /**
50520      * Returns the dataIndex for the specified column.
50521      * @param {Number} col The column index
50522      * @return {Number}
50523      */
50524     getDataIndex : function(col){
50525         return this.config[col].dataIndex;
50526     },
50527
50528     /**
50529      * Sets the dataIndex for a column.
50530      * @param {Number} col The column index
50531      * @param {Number} dataIndex The new dataIndex
50532      */
50533     setDataIndex : function(col, dataIndex){
50534         this.config[col].dataIndex = dataIndex;
50535     },
50536
50537     
50538     
50539     /**
50540      * Returns true if the cell is editable.
50541      * @param {Number} colIndex The column index
50542      * @param {Number} rowIndex The row index
50543      * @return {Boolean}
50544      */
50545     isCellEditable : function(colIndex, rowIndex){
50546         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50547     },
50548
50549     /**
50550      * Returns the editor defined for the cell/column.
50551      * return false or null to disable editing.
50552      * @param {Number} colIndex The column index
50553      * @param {Number} rowIndex The row index
50554      * @return {Object}
50555      */
50556     getCellEditor : function(colIndex, rowIndex){
50557         return this.config[colIndex].editor;
50558     },
50559
50560     /**
50561      * Sets if a column is editable.
50562      * @param {Number} col The column index
50563      * @param {Boolean} editable True if the column is editable
50564      */
50565     setEditable : function(col, editable){
50566         this.config[col].editable = editable;
50567     },
50568
50569
50570     /**
50571      * Returns true if the column is hidden.
50572      * @param {Number} colIndex The column index
50573      * @return {Boolean}
50574      */
50575     isHidden : function(colIndex){
50576         return this.config[colIndex].hidden;
50577     },
50578
50579
50580     /**
50581      * Returns true if the column width cannot be changed
50582      */
50583     isFixed : function(colIndex){
50584         return this.config[colIndex].fixed;
50585     },
50586
50587     /**
50588      * Returns true if the column can be resized
50589      * @return {Boolean}
50590      */
50591     isResizable : function(colIndex){
50592         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50593     },
50594     /**
50595      * Sets if a column is hidden.
50596      * @param {Number} colIndex The column index
50597      * @param {Boolean} hidden True if the column is hidden
50598      */
50599     setHidden : function(colIndex, hidden){
50600         this.config[colIndex].hidden = hidden;
50601         this.totalWidth = null;
50602         this.fireEvent("hiddenchange", this, colIndex, hidden);
50603     },
50604
50605     /**
50606      * Sets the editor for a column.
50607      * @param {Number} col The column index
50608      * @param {Object} editor The editor object
50609      */
50610     setEditor : function(col, editor){
50611         this.config[col].editor = editor;
50612     }
50613 });
50614
50615 Roo.grid.ColumnModel.defaultRenderer = function(value){
50616         if(typeof value == "string" && value.length < 1){
50617             return "&#160;";
50618         }
50619         return value;
50620 };
50621
50622 // Alias for backwards compatibility
50623 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50624 /*
50625  * Based on:
50626  * Ext JS Library 1.1.1
50627  * Copyright(c) 2006-2007, Ext JS, LLC.
50628  *
50629  * Originally Released Under LGPL - original licence link has changed is not relivant.
50630  *
50631  * Fork - LGPL
50632  * <script type="text/javascript">
50633  */
50634
50635 /**
50636  * @class Roo.grid.AbstractSelectionModel
50637  * @extends Roo.util.Observable
50638  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50639  * implemented by descendant classes.  This class should not be directly instantiated.
50640  * @constructor
50641  */
50642 Roo.grid.AbstractSelectionModel = function(){
50643     this.locked = false;
50644     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50645 };
50646
50647 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50648     /** @ignore Called by the grid automatically. Do not call directly. */
50649     init : function(grid){
50650         this.grid = grid;
50651         this.initEvents();
50652     },
50653
50654     /**
50655      * Locks the selections.
50656      */
50657     lock : function(){
50658         this.locked = true;
50659     },
50660
50661     /**
50662      * Unlocks the selections.
50663      */
50664     unlock : function(){
50665         this.locked = false;
50666     },
50667
50668     /**
50669      * Returns true if the selections are locked.
50670      * @return {Boolean}
50671      */
50672     isLocked : function(){
50673         return this.locked;
50674     }
50675 });/*
50676  * Based on:
50677  * Ext JS Library 1.1.1
50678  * Copyright(c) 2006-2007, Ext JS, LLC.
50679  *
50680  * Originally Released Under LGPL - original licence link has changed is not relivant.
50681  *
50682  * Fork - LGPL
50683  * <script type="text/javascript">
50684  */
50685 /**
50686  * @extends Roo.grid.AbstractSelectionModel
50687  * @class Roo.grid.RowSelectionModel
50688  * The default SelectionModel used by {@link Roo.grid.Grid}.
50689  * It supports multiple selections and keyboard selection/navigation. 
50690  * @constructor
50691  * @param {Object} config
50692  */
50693 Roo.grid.RowSelectionModel = function(config){
50694     Roo.apply(this, config);
50695     this.selections = new Roo.util.MixedCollection(false, function(o){
50696         return o.id;
50697     });
50698
50699     this.last = false;
50700     this.lastActive = false;
50701
50702     this.addEvents({
50703         /**
50704              * @event selectionchange
50705              * Fires when the selection changes
50706              * @param {SelectionModel} this
50707              */
50708             "selectionchange" : true,
50709         /**
50710              * @event afterselectionchange
50711              * Fires after the selection changes (eg. by key press or clicking)
50712              * @param {SelectionModel} this
50713              */
50714             "afterselectionchange" : true,
50715         /**
50716              * @event beforerowselect
50717              * Fires when a row is selected being selected, return false to cancel.
50718              * @param {SelectionModel} this
50719              * @param {Number} rowIndex The selected index
50720              * @param {Boolean} keepExisting False if other selections will be cleared
50721              */
50722             "beforerowselect" : true,
50723         /**
50724              * @event rowselect
50725              * Fires when a row is selected.
50726              * @param {SelectionModel} this
50727              * @param {Number} rowIndex The selected index
50728              * @param {Roo.data.Record} r The record
50729              */
50730             "rowselect" : true,
50731         /**
50732              * @event rowdeselect
50733              * Fires when a row is deselected.
50734              * @param {SelectionModel} this
50735              * @param {Number} rowIndex The selected index
50736              */
50737         "rowdeselect" : true
50738     });
50739     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50740     this.locked = false;
50741 };
50742
50743 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50744     /**
50745      * @cfg {Boolean} singleSelect
50746      * True to allow selection of only one row at a time (defaults to false)
50747      */
50748     singleSelect : false,
50749
50750     // private
50751     initEvents : function(){
50752
50753         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50754             this.grid.on("mousedown", this.handleMouseDown, this);
50755         }else{ // allow click to work like normal
50756             this.grid.on("rowclick", this.handleDragableRowClick, this);
50757         }
50758
50759         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50760             "up" : function(e){
50761                 if(!e.shiftKey){
50762                     this.selectPrevious(e.shiftKey);
50763                 }else if(this.last !== false && this.lastActive !== false){
50764                     var last = this.last;
50765                     this.selectRange(this.last,  this.lastActive-1);
50766                     this.grid.getView().focusRow(this.lastActive);
50767                     if(last !== false){
50768                         this.last = last;
50769                     }
50770                 }else{
50771                     this.selectFirstRow();
50772                 }
50773                 this.fireEvent("afterselectionchange", this);
50774             },
50775             "down" : function(e){
50776                 if(!e.shiftKey){
50777                     this.selectNext(e.shiftKey);
50778                 }else if(this.last !== false && this.lastActive !== false){
50779                     var last = this.last;
50780                     this.selectRange(this.last,  this.lastActive+1);
50781                     this.grid.getView().focusRow(this.lastActive);
50782                     if(last !== false){
50783                         this.last = last;
50784                     }
50785                 }else{
50786                     this.selectFirstRow();
50787                 }
50788                 this.fireEvent("afterselectionchange", this);
50789             },
50790             scope: this
50791         });
50792
50793         var view = this.grid.view;
50794         view.on("refresh", this.onRefresh, this);
50795         view.on("rowupdated", this.onRowUpdated, this);
50796         view.on("rowremoved", this.onRemove, this);
50797     },
50798
50799     // private
50800     onRefresh : function(){
50801         var ds = this.grid.dataSource, i, v = this.grid.view;
50802         var s = this.selections;
50803         s.each(function(r){
50804             if((i = ds.indexOfId(r.id)) != -1){
50805                 v.onRowSelect(i);
50806             }else{
50807                 s.remove(r);
50808             }
50809         });
50810     },
50811
50812     // private
50813     onRemove : function(v, index, r){
50814         this.selections.remove(r);
50815     },
50816
50817     // private
50818     onRowUpdated : function(v, index, r){
50819         if(this.isSelected(r)){
50820             v.onRowSelect(index);
50821         }
50822     },
50823
50824     /**
50825      * Select records.
50826      * @param {Array} records The records to select
50827      * @param {Boolean} keepExisting (optional) True to keep existing selections
50828      */
50829     selectRecords : function(records, keepExisting){
50830         if(!keepExisting){
50831             this.clearSelections();
50832         }
50833         var ds = this.grid.dataSource;
50834         for(var i = 0, len = records.length; i < len; i++){
50835             this.selectRow(ds.indexOf(records[i]), true);
50836         }
50837     },
50838
50839     /**
50840      * Gets the number of selected rows.
50841      * @return {Number}
50842      */
50843     getCount : function(){
50844         return this.selections.length;
50845     },
50846
50847     /**
50848      * Selects the first row in the grid.
50849      */
50850     selectFirstRow : function(){
50851         this.selectRow(0);
50852     },
50853
50854     /**
50855      * Select the last row.
50856      * @param {Boolean} keepExisting (optional) True to keep existing selections
50857      */
50858     selectLastRow : function(keepExisting){
50859         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50860     },
50861
50862     /**
50863      * Selects the row immediately following the last selected row.
50864      * @param {Boolean} keepExisting (optional) True to keep existing selections
50865      */
50866     selectNext : function(keepExisting){
50867         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50868             this.selectRow(this.last+1, keepExisting);
50869             this.grid.getView().focusRow(this.last);
50870         }
50871     },
50872
50873     /**
50874      * Selects the row that precedes the last selected row.
50875      * @param {Boolean} keepExisting (optional) True to keep existing selections
50876      */
50877     selectPrevious : function(keepExisting){
50878         if(this.last){
50879             this.selectRow(this.last-1, keepExisting);
50880             this.grid.getView().focusRow(this.last);
50881         }
50882     },
50883
50884     /**
50885      * Returns the selected records
50886      * @return {Array} Array of selected records
50887      */
50888     getSelections : function(){
50889         return [].concat(this.selections.items);
50890     },
50891
50892     /**
50893      * Returns the first selected record.
50894      * @return {Record}
50895      */
50896     getSelected : function(){
50897         return this.selections.itemAt(0);
50898     },
50899
50900
50901     /**
50902      * Clears all selections.
50903      */
50904     clearSelections : function(fast){
50905         if(this.locked) return;
50906         if(fast !== true){
50907             var ds = this.grid.dataSource;
50908             var s = this.selections;
50909             s.each(function(r){
50910                 this.deselectRow(ds.indexOfId(r.id));
50911             }, this);
50912             s.clear();
50913         }else{
50914             this.selections.clear();
50915         }
50916         this.last = false;
50917     },
50918
50919
50920     /**
50921      * Selects all rows.
50922      */
50923     selectAll : function(){
50924         if(this.locked) return;
50925         this.selections.clear();
50926         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50927             this.selectRow(i, true);
50928         }
50929     },
50930
50931     /**
50932      * Returns True if there is a selection.
50933      * @return {Boolean}
50934      */
50935     hasSelection : function(){
50936         return this.selections.length > 0;
50937     },
50938
50939     /**
50940      * Returns True if the specified row is selected.
50941      * @param {Number/Record} record The record or index of the record to check
50942      * @return {Boolean}
50943      */
50944     isSelected : function(index){
50945         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50946         return (r && this.selections.key(r.id) ? true : false);
50947     },
50948
50949     /**
50950      * Returns True if the specified record id is selected.
50951      * @param {String} id The id of record to check
50952      * @return {Boolean}
50953      */
50954     isIdSelected : function(id){
50955         return (this.selections.key(id) ? true : false);
50956     },
50957
50958     // private
50959     handleMouseDown : function(e, t){
50960         var view = this.grid.getView(), rowIndex;
50961         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50962             return;
50963         };
50964         if(e.shiftKey && this.last !== false){
50965             var last = this.last;
50966             this.selectRange(last, rowIndex, e.ctrlKey);
50967             this.last = last; // reset the last
50968             view.focusRow(rowIndex);
50969         }else{
50970             var isSelected = this.isSelected(rowIndex);
50971             if(e.button !== 0 && isSelected){
50972                 view.focusRow(rowIndex);
50973             }else if(e.ctrlKey && isSelected){
50974                 this.deselectRow(rowIndex);
50975             }else if(!isSelected){
50976                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50977                 view.focusRow(rowIndex);
50978             }
50979         }
50980         this.fireEvent("afterselectionchange", this);
50981     },
50982     // private
50983     handleDragableRowClick :  function(grid, rowIndex, e) 
50984     {
50985         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50986             this.selectRow(rowIndex, false);
50987             grid.view.focusRow(rowIndex);
50988              this.fireEvent("afterselectionchange", this);
50989         }
50990     },
50991     
50992     /**
50993      * Selects multiple rows.
50994      * @param {Array} rows Array of the indexes of the row to select
50995      * @param {Boolean} keepExisting (optional) True to keep existing selections
50996      */
50997     selectRows : function(rows, keepExisting){
50998         if(!keepExisting){
50999             this.clearSelections();
51000         }
51001         for(var i = 0, len = rows.length; i < len; i++){
51002             this.selectRow(rows[i], true);
51003         }
51004     },
51005
51006     /**
51007      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51008      * @param {Number} startRow The index of the first row in the range
51009      * @param {Number} endRow The index of the last row in the range
51010      * @param {Boolean} keepExisting (optional) True to retain existing selections
51011      */
51012     selectRange : function(startRow, endRow, keepExisting){
51013         if(this.locked) return;
51014         if(!keepExisting){
51015             this.clearSelections();
51016         }
51017         if(startRow <= endRow){
51018             for(var i = startRow; i <= endRow; i++){
51019                 this.selectRow(i, true);
51020             }
51021         }else{
51022             for(var i = startRow; i >= endRow; i--){
51023                 this.selectRow(i, true);
51024             }
51025         }
51026     },
51027
51028     /**
51029      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51030      * @param {Number} startRow The index of the first row in the range
51031      * @param {Number} endRow The index of the last row in the range
51032      */
51033     deselectRange : function(startRow, endRow, preventViewNotify){
51034         if(this.locked) return;
51035         for(var i = startRow; i <= endRow; i++){
51036             this.deselectRow(i, preventViewNotify);
51037         }
51038     },
51039
51040     /**
51041      * Selects a row.
51042      * @param {Number} row The index of the row to select
51043      * @param {Boolean} keepExisting (optional) True to keep existing selections
51044      */
51045     selectRow : function(index, keepExisting, preventViewNotify){
51046         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51047         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51048             if(!keepExisting || this.singleSelect){
51049                 this.clearSelections();
51050             }
51051             var r = this.grid.dataSource.getAt(index);
51052             this.selections.add(r);
51053             this.last = this.lastActive = index;
51054             if(!preventViewNotify){
51055                 this.grid.getView().onRowSelect(index);
51056             }
51057             this.fireEvent("rowselect", this, index, r);
51058             this.fireEvent("selectionchange", this);
51059         }
51060     },
51061
51062     /**
51063      * Deselects a row.
51064      * @param {Number} row The index of the row to deselect
51065      */
51066     deselectRow : function(index, preventViewNotify){
51067         if(this.locked) return;
51068         if(this.last == index){
51069             this.last = false;
51070         }
51071         if(this.lastActive == index){
51072             this.lastActive = false;
51073         }
51074         var r = this.grid.dataSource.getAt(index);
51075         this.selections.remove(r);
51076         if(!preventViewNotify){
51077             this.grid.getView().onRowDeselect(index);
51078         }
51079         this.fireEvent("rowdeselect", this, index);
51080         this.fireEvent("selectionchange", this);
51081     },
51082
51083     // private
51084     restoreLast : function(){
51085         if(this._last){
51086             this.last = this._last;
51087         }
51088     },
51089
51090     // private
51091     acceptsNav : function(row, col, cm){
51092         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51093     },
51094
51095     // private
51096     onEditorKey : function(field, e){
51097         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51098         if(k == e.TAB){
51099             e.stopEvent();
51100             ed.completeEdit();
51101             if(e.shiftKey){
51102                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51103             }else{
51104                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51105             }
51106         }else if(k == e.ENTER && !e.ctrlKey){
51107             e.stopEvent();
51108             ed.completeEdit();
51109             if(e.shiftKey){
51110                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51111             }else{
51112                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51113             }
51114         }else if(k == e.ESC){
51115             ed.cancelEdit();
51116         }
51117         if(newCell){
51118             g.startEditing(newCell[0], newCell[1]);
51119         }
51120     }
51121 });/*
51122  * Based on:
51123  * Ext JS Library 1.1.1
51124  * Copyright(c) 2006-2007, Ext JS, LLC.
51125  *
51126  * Originally Released Under LGPL - original licence link has changed is not relivant.
51127  *
51128  * Fork - LGPL
51129  * <script type="text/javascript">
51130  */
51131 /**
51132  * @class Roo.grid.CellSelectionModel
51133  * @extends Roo.grid.AbstractSelectionModel
51134  * This class provides the basic implementation for cell selection in a grid.
51135  * @constructor
51136  * @param {Object} config The object containing the configuration of this model.
51137  */
51138 Roo.grid.CellSelectionModel = function(config){
51139     Roo.apply(this, config);
51140
51141     this.selection = null;
51142
51143     this.addEvents({
51144         /**
51145              * @event beforerowselect
51146              * Fires before a cell is selected.
51147              * @param {SelectionModel} this
51148              * @param {Number} rowIndex The selected row index
51149              * @param {Number} colIndex The selected cell index
51150              */
51151             "beforecellselect" : true,
51152         /**
51153              * @event cellselect
51154              * Fires when a cell is selected.
51155              * @param {SelectionModel} this
51156              * @param {Number} rowIndex The selected row index
51157              * @param {Number} colIndex The selected cell index
51158              */
51159             "cellselect" : true,
51160         /**
51161              * @event selectionchange
51162              * Fires when the active selection changes.
51163              * @param {SelectionModel} this
51164              * @param {Object} selection null for no selection or an object (o) with two properties
51165                 <ul>
51166                 <li>o.record: the record object for the row the selection is in</li>
51167                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51168                 </ul>
51169              */
51170             "selectionchange" : true
51171     });
51172     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51173 };
51174
51175 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51176
51177     /** @ignore */
51178     initEvents : function(){
51179         this.grid.on("mousedown", this.handleMouseDown, this);
51180         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51181         var view = this.grid.view;
51182         view.on("refresh", this.onViewChange, this);
51183         view.on("rowupdated", this.onRowUpdated, this);
51184         view.on("beforerowremoved", this.clearSelections, this);
51185         view.on("beforerowsinserted", this.clearSelections, this);
51186         if(this.grid.isEditor){
51187             this.grid.on("beforeedit", this.beforeEdit,  this);
51188         }
51189     },
51190
51191         //private
51192     beforeEdit : function(e){
51193         this.select(e.row, e.column, false, true, e.record);
51194     },
51195
51196         //private
51197     onRowUpdated : function(v, index, r){
51198         if(this.selection && this.selection.record == r){
51199             v.onCellSelect(index, this.selection.cell[1]);
51200         }
51201     },
51202
51203         //private
51204     onViewChange : function(){
51205         this.clearSelections(true);
51206     },
51207
51208         /**
51209          * Returns the currently selected cell,.
51210          * @return {Array} The selected cell (row, column) or null if none selected.
51211          */
51212     getSelectedCell : function(){
51213         return this.selection ? this.selection.cell : null;
51214     },
51215
51216     /**
51217      * Clears all selections.
51218      * @param {Boolean} true to prevent the gridview from being notified about the change.
51219      */
51220     clearSelections : function(preventNotify){
51221         var s = this.selection;
51222         if(s){
51223             if(preventNotify !== true){
51224                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51225             }
51226             this.selection = null;
51227             this.fireEvent("selectionchange", this, null);
51228         }
51229     },
51230
51231     /**
51232      * Returns true if there is a selection.
51233      * @return {Boolean}
51234      */
51235     hasSelection : function(){
51236         return this.selection ? true : false;
51237     },
51238
51239     /** @ignore */
51240     handleMouseDown : function(e, t){
51241         var v = this.grid.getView();
51242         if(this.isLocked()){
51243             return;
51244         };
51245         var row = v.findRowIndex(t);
51246         var cell = v.findCellIndex(t);
51247         if(row !== false && cell !== false){
51248             this.select(row, cell);
51249         }
51250     },
51251
51252     /**
51253      * Selects a cell.
51254      * @param {Number} rowIndex
51255      * @param {Number} collIndex
51256      */
51257     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51258         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51259             this.clearSelections();
51260             r = r || this.grid.dataSource.getAt(rowIndex);
51261             this.selection = {
51262                 record : r,
51263                 cell : [rowIndex, colIndex]
51264             };
51265             if(!preventViewNotify){
51266                 var v = this.grid.getView();
51267                 v.onCellSelect(rowIndex, colIndex);
51268                 if(preventFocus !== true){
51269                     v.focusCell(rowIndex, colIndex);
51270                 }
51271             }
51272             this.fireEvent("cellselect", this, rowIndex, colIndex);
51273             this.fireEvent("selectionchange", this, this.selection);
51274         }
51275     },
51276
51277         //private
51278     isSelectable : function(rowIndex, colIndex, cm){
51279         return !cm.isHidden(colIndex);
51280     },
51281
51282     /** @ignore */
51283     handleKeyDown : function(e){
51284         //Roo.log('Cell Sel Model handleKeyDown');
51285         if(!e.isNavKeyPress()){
51286             return;
51287         }
51288         var g = this.grid, s = this.selection;
51289         if(!s){
51290             e.stopEvent();
51291             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51292             if(cell){
51293                 this.select(cell[0], cell[1]);
51294             }
51295             return;
51296         }
51297         var sm = this;
51298         var walk = function(row, col, step){
51299             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51300         };
51301         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51302         var newCell;
51303
51304         switch(k){
51305             case e.TAB:
51306                 // handled by onEditorKey
51307                 if (g.isEditor && g.editing) {
51308                     return;
51309                 }
51310                 if(e.shiftKey){
51311                      newCell = walk(r, c-1, -1);
51312                 }else{
51313                      newCell = walk(r, c+1, 1);
51314                 }
51315              break;
51316              case e.DOWN:
51317                  newCell = walk(r+1, c, 1);
51318              break;
51319              case e.UP:
51320                  newCell = walk(r-1, c, -1);
51321              break;
51322              case e.RIGHT:
51323                  newCell = walk(r, c+1, 1);
51324              break;
51325              case e.LEFT:
51326                  newCell = walk(r, c-1, -1);
51327              break;
51328              case e.ENTER:
51329                  if(g.isEditor && !g.editing){
51330                     g.startEditing(r, c);
51331                     e.stopEvent();
51332                     return;
51333                 }
51334              break;
51335         };
51336         if(newCell){
51337             this.select(newCell[0], newCell[1]);
51338             e.stopEvent();
51339         }
51340     },
51341
51342     acceptsNav : function(row, col, cm){
51343         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51344     },
51345     /**
51346      * Selects a cell.
51347      * @param {Number} field (not used) - as it's normally used as a listener
51348      * @param {Number} e - event - fake it by using
51349      *
51350      * var e = Roo.EventObjectImpl.prototype;
51351      * e.keyCode = e.TAB
51352      *
51353      * 
51354      */
51355     onEditorKey : function(field, e){
51356         
51357         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51358         ///Roo.log('onEditorKey' + k);
51359         if (!ed) {
51360             
51361             
51362             
51363         }
51364         if(k == e.TAB){
51365             if(e.shiftKey){
51366                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51367             }else{
51368                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51369             }
51370             
51371             e.stopEvent();
51372             
51373         }else if(k == e.ENTER &&  !e.ctrlKey){
51374             ed.completeEdit();
51375             e.stopEvent();
51376             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51377         }else if(k == e.ESC){
51378             ed.cancelEdit();
51379         }
51380         
51381         
51382         if(newCell){
51383             //Roo.log('next cell after edit');
51384             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51385         }
51386     }
51387 });/*
51388  * Based on:
51389  * Ext JS Library 1.1.1
51390  * Copyright(c) 2006-2007, Ext JS, LLC.
51391  *
51392  * Originally Released Under LGPL - original licence link has changed is not relivant.
51393  *
51394  * Fork - LGPL
51395  * <script type="text/javascript">
51396  */
51397  
51398 /**
51399  * @class Roo.grid.EditorGrid
51400  * @extends Roo.grid.Grid
51401  * Class for creating and editable grid.
51402  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51403  * The container MUST have some type of size defined for the grid to fill. The container will be 
51404  * automatically set to position relative if it isn't already.
51405  * @param {Object} dataSource The data model to bind to
51406  * @param {Object} colModel The column model with info about this grid's columns
51407  */
51408 Roo.grid.EditorGrid = function(container, config){
51409     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51410     this.getGridEl().addClass("xedit-grid");
51411
51412     if(!this.selModel){
51413         this.selModel = new Roo.grid.CellSelectionModel();
51414     }
51415
51416     this.activeEditor = null;
51417
51418         this.addEvents({
51419             /**
51420              * @event beforeedit
51421              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51422              * <ul style="padding:5px;padding-left:16px;">
51423              * <li>grid - This grid</li>
51424              * <li>record - The record being edited</li>
51425              * <li>field - The field name being edited</li>
51426              * <li>value - The value for the field being edited.</li>
51427              * <li>row - The grid row index</li>
51428              * <li>column - The grid column index</li>
51429              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51430              * </ul>
51431              * @param {Object} e An edit event (see above for description)
51432              */
51433             "beforeedit" : true,
51434             /**
51435              * @event afteredit
51436              * Fires after a cell is edited. <br />
51437              * <ul style="padding:5px;padding-left:16px;">
51438              * <li>grid - This grid</li>
51439              * <li>record - The record being edited</li>
51440              * <li>field - The field name being edited</li>
51441              * <li>value - The value being set</li>
51442              * <li>originalValue - The original value for the field, before the edit.</li>
51443              * <li>row - The grid row index</li>
51444              * <li>column - The grid column index</li>
51445              * </ul>
51446              * @param {Object} e An edit event (see above for description)
51447              */
51448             "afteredit" : true,
51449             /**
51450              * @event validateedit
51451              * Fires after a cell is edited, but before the value is set in the record. 
51452          * You can use this to modify the value being set in the field, Return false
51453              * to cancel the change. The edit event object has the following properties <br />
51454              * <ul style="padding:5px;padding-left:16px;">
51455          * <li>editor - This editor</li>
51456              * <li>grid - This grid</li>
51457              * <li>record - The record being edited</li>
51458              * <li>field - The field name being edited</li>
51459              * <li>value - The value being set</li>
51460              * <li>originalValue - The original value for the field, before the edit.</li>
51461              * <li>row - The grid row index</li>
51462              * <li>column - The grid column index</li>
51463              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51464              * </ul>
51465              * @param {Object} e An edit event (see above for description)
51466              */
51467             "validateedit" : true
51468         });
51469     this.on("bodyscroll", this.stopEditing,  this);
51470     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51471 };
51472
51473 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51474     /**
51475      * @cfg {Number} clicksToEdit
51476      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51477      */
51478     clicksToEdit: 2,
51479
51480     // private
51481     isEditor : true,
51482     // private
51483     trackMouseOver: false, // causes very odd FF errors
51484
51485     onCellDblClick : function(g, row, col){
51486         this.startEditing(row, col);
51487     },
51488
51489     onEditComplete : function(ed, value, startValue){
51490         this.editing = false;
51491         this.activeEditor = null;
51492         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51493         var r = ed.record;
51494         var field = this.colModel.getDataIndex(ed.col);
51495         var e = {
51496             grid: this,
51497             record: r,
51498             field: field,
51499             originalValue: startValue,
51500             value: value,
51501             row: ed.row,
51502             column: ed.col,
51503             cancel:false,
51504             editor: ed
51505         };
51506         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51507         cell.show();
51508           
51509         if(String(value) !== String(startValue)){
51510             
51511             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51512                 r.set(field, e.value);
51513                 // if we are dealing with a combo box..
51514                 // then we also set the 'name' colum to be the displayField
51515                 if (ed.field.displayField && ed.field.name) {
51516                     r.set(ed.field.name, ed.field.el.dom.value);
51517                 }
51518                 
51519                 delete e.cancel; //?? why!!!
51520                 this.fireEvent("afteredit", e);
51521             }
51522         } else {
51523             this.fireEvent("afteredit", e); // always fire it!
51524         }
51525         this.view.focusCell(ed.row, ed.col);
51526     },
51527
51528     /**
51529      * Starts editing the specified for the specified row/column
51530      * @param {Number} rowIndex
51531      * @param {Number} colIndex
51532      */
51533     startEditing : function(row, col){
51534         this.stopEditing();
51535         if(this.colModel.isCellEditable(col, row)){
51536             this.view.ensureVisible(row, col, true);
51537           
51538             var r = this.dataSource.getAt(row);
51539             var field = this.colModel.getDataIndex(col);
51540             var cell = Roo.get(this.view.getCell(row,col));
51541             var e = {
51542                 grid: this,
51543                 record: r,
51544                 field: field,
51545                 value: r.data[field],
51546                 row: row,
51547                 column: col,
51548                 cancel:false 
51549             };
51550             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51551                 this.editing = true;
51552                 var ed = this.colModel.getCellEditor(col, row);
51553                 
51554                 if (!ed) {
51555                     return;
51556                 }
51557                 if(!ed.rendered){
51558                     ed.render(ed.parentEl || document.body);
51559                 }
51560                 ed.field.reset();
51561                
51562                 cell.hide();
51563                 
51564                 (function(){ // complex but required for focus issues in safari, ie and opera
51565                     ed.row = row;
51566                     ed.col = col;
51567                     ed.record = r;
51568                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51569                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51570                     this.activeEditor = ed;
51571                     var v = r.data[field];
51572                     ed.startEdit(this.view.getCell(row, col), v);
51573                     // combo's with 'displayField and name set
51574                     if (ed.field.displayField && ed.field.name) {
51575                         ed.field.el.dom.value = r.data[ed.field.name];
51576                     }
51577                     
51578                     
51579                 }).defer(50, this);
51580             }
51581         }
51582     },
51583         
51584     /**
51585      * Stops any active editing
51586      */
51587     stopEditing : function(){
51588         if(this.activeEditor){
51589             this.activeEditor.completeEdit();
51590         }
51591         this.activeEditor = null;
51592     }
51593 });/*
51594  * Based on:
51595  * Ext JS Library 1.1.1
51596  * Copyright(c) 2006-2007, Ext JS, LLC.
51597  *
51598  * Originally Released Under LGPL - original licence link has changed is not relivant.
51599  *
51600  * Fork - LGPL
51601  * <script type="text/javascript">
51602  */
51603
51604 // private - not really -- you end up using it !
51605 // This is a support class used internally by the Grid components
51606
51607 /**
51608  * @class Roo.grid.GridEditor
51609  * @extends Roo.Editor
51610  * Class for creating and editable grid elements.
51611  * @param {Object} config any settings (must include field)
51612  */
51613 Roo.grid.GridEditor = function(field, config){
51614     if (!config && field.field) {
51615         config = field;
51616         field = Roo.factory(config.field, Roo.form);
51617     }
51618     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51619     field.monitorTab = false;
51620 };
51621
51622 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51623     
51624     /**
51625      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51626      */
51627     
51628     alignment: "tl-tl",
51629     autoSize: "width",
51630     hideEl : false,
51631     cls: "x-small-editor x-grid-editor",
51632     shim:false,
51633     shadow:"frame"
51634 });/*
51635  * Based on:
51636  * Ext JS Library 1.1.1
51637  * Copyright(c) 2006-2007, Ext JS, LLC.
51638  *
51639  * Originally Released Under LGPL - original licence link has changed is not relivant.
51640  *
51641  * Fork - LGPL
51642  * <script type="text/javascript">
51643  */
51644   
51645
51646   
51647 Roo.grid.PropertyRecord = Roo.data.Record.create([
51648     {name:'name',type:'string'},  'value'
51649 ]);
51650
51651
51652 Roo.grid.PropertyStore = function(grid, source){
51653     this.grid = grid;
51654     this.store = new Roo.data.Store({
51655         recordType : Roo.grid.PropertyRecord
51656     });
51657     this.store.on('update', this.onUpdate,  this);
51658     if(source){
51659         this.setSource(source);
51660     }
51661     Roo.grid.PropertyStore.superclass.constructor.call(this);
51662 };
51663
51664
51665
51666 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51667     setSource : function(o){
51668         this.source = o;
51669         this.store.removeAll();
51670         var data = [];
51671         for(var k in o){
51672             if(this.isEditableValue(o[k])){
51673                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51674             }
51675         }
51676         this.store.loadRecords({records: data}, {}, true);
51677     },
51678
51679     onUpdate : function(ds, record, type){
51680         if(type == Roo.data.Record.EDIT){
51681             var v = record.data['value'];
51682             var oldValue = record.modified['value'];
51683             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51684                 this.source[record.id] = v;
51685                 record.commit();
51686                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51687             }else{
51688                 record.reject();
51689             }
51690         }
51691     },
51692
51693     getProperty : function(row){
51694        return this.store.getAt(row);
51695     },
51696
51697     isEditableValue: function(val){
51698         if(val && val instanceof Date){
51699             return true;
51700         }else if(typeof val == 'object' || typeof val == 'function'){
51701             return false;
51702         }
51703         return true;
51704     },
51705
51706     setValue : function(prop, value){
51707         this.source[prop] = value;
51708         this.store.getById(prop).set('value', value);
51709     },
51710
51711     getSource : function(){
51712         return this.source;
51713     }
51714 });
51715
51716 Roo.grid.PropertyColumnModel = function(grid, store){
51717     this.grid = grid;
51718     var g = Roo.grid;
51719     g.PropertyColumnModel.superclass.constructor.call(this, [
51720         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51721         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51722     ]);
51723     this.store = store;
51724     this.bselect = Roo.DomHelper.append(document.body, {
51725         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51726             {tag: 'option', value: 'true', html: 'true'},
51727             {tag: 'option', value: 'false', html: 'false'}
51728         ]
51729     });
51730     Roo.id(this.bselect);
51731     var f = Roo.form;
51732     this.editors = {
51733         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51734         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51735         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51736         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51737         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51738     };
51739     this.renderCellDelegate = this.renderCell.createDelegate(this);
51740     this.renderPropDelegate = this.renderProp.createDelegate(this);
51741 };
51742
51743 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51744     
51745     
51746     nameText : 'Name',
51747     valueText : 'Value',
51748     
51749     dateFormat : 'm/j/Y',
51750     
51751     
51752     renderDate : function(dateVal){
51753         return dateVal.dateFormat(this.dateFormat);
51754     },
51755
51756     renderBool : function(bVal){
51757         return bVal ? 'true' : 'false';
51758     },
51759
51760     isCellEditable : function(colIndex, rowIndex){
51761         return colIndex == 1;
51762     },
51763
51764     getRenderer : function(col){
51765         return col == 1 ?
51766             this.renderCellDelegate : this.renderPropDelegate;
51767     },
51768
51769     renderProp : function(v){
51770         return this.getPropertyName(v);
51771     },
51772
51773     renderCell : function(val){
51774         var rv = val;
51775         if(val instanceof Date){
51776             rv = this.renderDate(val);
51777         }else if(typeof val == 'boolean'){
51778             rv = this.renderBool(val);
51779         }
51780         return Roo.util.Format.htmlEncode(rv);
51781     },
51782
51783     getPropertyName : function(name){
51784         var pn = this.grid.propertyNames;
51785         return pn && pn[name] ? pn[name] : name;
51786     },
51787
51788     getCellEditor : function(colIndex, rowIndex){
51789         var p = this.store.getProperty(rowIndex);
51790         var n = p.data['name'], val = p.data['value'];
51791         
51792         if(typeof(this.grid.customEditors[n]) == 'string'){
51793             return this.editors[this.grid.customEditors[n]];
51794         }
51795         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51796             return this.grid.customEditors[n];
51797         }
51798         if(val instanceof Date){
51799             return this.editors['date'];
51800         }else if(typeof val == 'number'){
51801             return this.editors['number'];
51802         }else if(typeof val == 'boolean'){
51803             return this.editors['boolean'];
51804         }else{
51805             return this.editors['string'];
51806         }
51807     }
51808 });
51809
51810 /**
51811  * @class Roo.grid.PropertyGrid
51812  * @extends Roo.grid.EditorGrid
51813  * This class represents the  interface of a component based property grid control.
51814  * <br><br>Usage:<pre><code>
51815  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51816       
51817  });
51818  // set any options
51819  grid.render();
51820  * </code></pre>
51821   
51822  * @constructor
51823  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51824  * The container MUST have some type of size defined for the grid to fill. The container will be
51825  * automatically set to position relative if it isn't already.
51826  * @param {Object} config A config object that sets properties on this grid.
51827  */
51828 Roo.grid.PropertyGrid = function(container, config){
51829     config = config || {};
51830     var store = new Roo.grid.PropertyStore(this);
51831     this.store = store;
51832     var cm = new Roo.grid.PropertyColumnModel(this, store);
51833     store.store.sort('name', 'ASC');
51834     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51835         ds: store.store,
51836         cm: cm,
51837         enableColLock:false,
51838         enableColumnMove:false,
51839         stripeRows:false,
51840         trackMouseOver: false,
51841         clicksToEdit:1
51842     }, config));
51843     this.getGridEl().addClass('x-props-grid');
51844     this.lastEditRow = null;
51845     this.on('columnresize', this.onColumnResize, this);
51846     this.addEvents({
51847          /**
51848              * @event beforepropertychange
51849              * Fires before a property changes (return false to stop?)
51850              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51851              * @param {String} id Record Id
51852              * @param {String} newval New Value
51853          * @param {String} oldval Old Value
51854              */
51855         "beforepropertychange": true,
51856         /**
51857              * @event propertychange
51858              * Fires after a property changes
51859              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51860              * @param {String} id Record Id
51861              * @param {String} newval New Value
51862          * @param {String} oldval Old Value
51863              */
51864         "propertychange": true
51865     });
51866     this.customEditors = this.customEditors || {};
51867 };
51868 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51869     
51870      /**
51871      * @cfg {Object} customEditors map of colnames=> custom editors.
51872      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51873      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51874      * false disables editing of the field.
51875          */
51876     
51877       /**
51878      * @cfg {Object} propertyNames map of property Names to their displayed value
51879          */
51880     
51881     render : function(){
51882         Roo.grid.PropertyGrid.superclass.render.call(this);
51883         this.autoSize.defer(100, this);
51884     },
51885
51886     autoSize : function(){
51887         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51888         if(this.view){
51889             this.view.fitColumns();
51890         }
51891     },
51892
51893     onColumnResize : function(){
51894         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51895         this.autoSize();
51896     },
51897     /**
51898      * Sets the data for the Grid
51899      * accepts a Key => Value object of all the elements avaiable.
51900      * @param {Object} data  to appear in grid.
51901      */
51902     setSource : function(source){
51903         this.store.setSource(source);
51904         //this.autoSize();
51905     },
51906     /**
51907      * Gets all the data from the grid.
51908      * @return {Object} data  data stored in grid
51909      */
51910     getSource : function(){
51911         return this.store.getSource();
51912     }
51913 });/*
51914  * Based on:
51915  * Ext JS Library 1.1.1
51916  * Copyright(c) 2006-2007, Ext JS, LLC.
51917  *
51918  * Originally Released Under LGPL - original licence link has changed is not relivant.
51919  *
51920  * Fork - LGPL
51921  * <script type="text/javascript">
51922  */
51923  
51924 /**
51925  * @class Roo.LoadMask
51926  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51927  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51928  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51929  * element's UpdateManager load indicator and will be destroyed after the initial load.
51930  * @constructor
51931  * Create a new LoadMask
51932  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51933  * @param {Object} config The config object
51934  */
51935 Roo.LoadMask = function(el, config){
51936     this.el = Roo.get(el);
51937     Roo.apply(this, config);
51938     if(this.store){
51939         this.store.on('beforeload', this.onBeforeLoad, this);
51940         this.store.on('load', this.onLoad, this);
51941         this.store.on('loadexception', this.onLoad, this);
51942         this.removeMask = false;
51943     }else{
51944         var um = this.el.getUpdateManager();
51945         um.showLoadIndicator = false; // disable the default indicator
51946         um.on('beforeupdate', this.onBeforeLoad, this);
51947         um.on('update', this.onLoad, this);
51948         um.on('failure', this.onLoad, this);
51949         this.removeMask = true;
51950     }
51951 };
51952
51953 Roo.LoadMask.prototype = {
51954     /**
51955      * @cfg {Boolean} removeMask
51956      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51957      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51958      */
51959     /**
51960      * @cfg {String} msg
51961      * The text to display in a centered loading message box (defaults to 'Loading...')
51962      */
51963     msg : 'Loading...',
51964     /**
51965      * @cfg {String} msgCls
51966      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51967      */
51968     msgCls : 'x-mask-loading',
51969
51970     /**
51971      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51972      * @type Boolean
51973      */
51974     disabled: false,
51975
51976     /**
51977      * Disables the mask to prevent it from being displayed
51978      */
51979     disable : function(){
51980        this.disabled = true;
51981     },
51982
51983     /**
51984      * Enables the mask so that it can be displayed
51985      */
51986     enable : function(){
51987         this.disabled = false;
51988     },
51989
51990     // private
51991     onLoad : function(){
51992         this.el.unmask(this.removeMask);
51993     },
51994
51995     // private
51996     onBeforeLoad : function(){
51997         if(!this.disabled){
51998             this.el.mask(this.msg, this.msgCls);
51999         }
52000     },
52001
52002     // private
52003     destroy : function(){
52004         if(this.store){
52005             this.store.un('beforeload', this.onBeforeLoad, this);
52006             this.store.un('load', this.onLoad, this);
52007             this.store.un('loadexception', this.onLoad, this);
52008         }else{
52009             var um = this.el.getUpdateManager();
52010             um.un('beforeupdate', this.onBeforeLoad, this);
52011             um.un('update', this.onLoad, this);
52012             um.un('failure', this.onLoad, this);
52013         }
52014     }
52015 };/*
52016  * Based on:
52017  * Ext JS Library 1.1.1
52018  * Copyright(c) 2006-2007, Ext JS, LLC.
52019  *
52020  * Originally Released Under LGPL - original licence link has changed is not relivant.
52021  *
52022  * Fork - LGPL
52023  * <script type="text/javascript">
52024  */
52025 Roo.XTemplate = function(){
52026     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52027     var s = this.html;
52028
52029     s = ['<tpl>', s, '</tpl>'].join('');
52030
52031     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52032
52033     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52034     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52035     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52036     var m, id = 0;
52037     var tpls = [];
52038
52039     while(m = s.match(re)){
52040        var m2 = m[0].match(nameRe);
52041        var m3 = m[0].match(ifRe);
52042        var m4 = m[0].match(execRe);
52043        var exp = null, fn = null, exec = null;
52044        var name = m2 && m2[1] ? m2[1] : '';
52045        if(m3){
52046            exp = m3 && m3[1] ? m3[1] : null;
52047            if(exp){
52048                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52049            }
52050        }
52051        if(m4){
52052            exp = m4 && m4[1] ? m4[1] : null;
52053            if(exp){
52054                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52055            }
52056        }
52057        if(name){
52058            switch(name){
52059                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52060                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52061                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52062            }
52063        }
52064        tpls.push({
52065             id: id,
52066             target: name,
52067             exec: exec,
52068             test: fn,
52069             body: m[1]||''
52070         });
52071        s = s.replace(m[0], '{xtpl'+ id + '}');
52072        ++id;
52073     }
52074     for(var i = tpls.length-1; i >= 0; --i){
52075         this.compileTpl(tpls[i]);
52076     }
52077     this.master = tpls[tpls.length-1];
52078     this.tpls = tpls;
52079 };
52080 Roo.extend(Roo.XTemplate, Roo.Template, {
52081
52082     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52083
52084     applySubTemplate : function(id, values, parent){
52085         var t = this.tpls[id];
52086         if(t.test && !t.test.call(this, values, parent)){
52087             return '';
52088         }
52089         if(t.exec && t.exec.call(this, values, parent)){
52090             return '';
52091         }
52092         var vs = t.target ? t.target.call(this, values, parent) : values;
52093         parent = t.target ? values : parent;
52094         if(t.target && vs instanceof Array){
52095             var buf = [];
52096             for(var i = 0, len = vs.length; i < len; i++){
52097                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52098             }
52099             return buf.join('');
52100         }
52101         return t.compiled.call(this, vs, parent);
52102     },
52103
52104     compileTpl : function(tpl){
52105         var fm = Roo.util.Format;
52106         var useF = this.disableFormats !== true;
52107         var sep = Roo.isGecko ? "+" : ",";
52108         var fn = function(m, name, format, args){
52109             if(name.substr(0, 4) == 'xtpl'){
52110                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52111             }
52112             var v;
52113             if(name.indexOf('.') != -1){
52114                 v = name;
52115             }else{
52116                 v = "values['" + name + "']";
52117             }
52118             if(format && useF){
52119                 args = args ? ',' + args : "";
52120                 if(format.substr(0, 5) != "this."){
52121                     format = "fm." + format + '(';
52122                 }else{
52123                     format = 'this.call("'+ format.substr(5) + '", ';
52124                     args = ", values";
52125                 }
52126             }else{
52127                 args= ''; format = "("+v+" === undefined ? '' : ";
52128             }
52129             return "'"+ sep + format + v + args + ")"+sep+"'";
52130         };
52131         var body;
52132         // branched to use + in gecko and [].join() in others
52133         if(Roo.isGecko){
52134             body = "tpl.compiled = function(values, parent){ return '" +
52135                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52136                     "';};";
52137         }else{
52138             body = ["tpl.compiled = function(values, parent){ return ['"];
52139             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52140             body.push("'].join('');};");
52141             body = body.join('');
52142         }
52143         /** eval:var:zzzzzzz */
52144         eval(body);
52145         return this;
52146     },
52147
52148     applyTemplate : function(values){
52149         return this.master.compiled.call(this, values, {});
52150         var s = this.subs;
52151     },
52152
52153     apply : function(){
52154         return this.applyTemplate.apply(this, arguments);
52155     },
52156
52157     compile : function(){return this;}
52158 });
52159
52160 Roo.XTemplate.from = function(el){
52161     el = Roo.getDom(el);
52162     return new Roo.XTemplate(el.value || el.innerHTML);
52163 };/*
52164  * Original code for Roojs - LGPL
52165  * <script type="text/javascript">
52166  */
52167  
52168 /**
52169  * @class Roo.XComponent
52170  * A delayed Element creator...
52171  * Or a way to group chunks of interface together.
52172  * 
52173  * Mypart.xyx = new Roo.XComponent({
52174
52175     parent : 'Mypart.xyz', // empty == document.element.!!
52176     order : '001',
52177     name : 'xxxx'
52178     region : 'xxxx'
52179     disabled : function() {} 
52180      
52181     tree : function() { // return an tree of xtype declared components
52182         var MODULE = this;
52183         return 
52184         {
52185             xtype : 'NestedLayoutPanel',
52186             // technicall
52187         }
52188      ]
52189  *})
52190  *
52191  *
52192  * It can be used to build a big heiracy, with parent etc.
52193  * or you can just use this to render a single compoent to a dom element
52194  * MYPART.render(Roo.Element | String(id) | dom_element )
52195  * 
52196  * @extends Roo.util.Observable
52197  * @constructor
52198  * @param cfg {Object} configuration of component
52199  * 
52200  */
52201 Roo.XComponent = function(cfg) {
52202     Roo.apply(this, cfg);
52203     this.addEvents({ 
52204         /**
52205              * @event built
52206              * Fires when this the componnt is built
52207              * @param {Roo.XComponent} c the component
52208              */
52209         'built' : true,
52210         /**
52211              * @event buildcomplete
52212              * Fires on the top level element when all elements have been built
52213              * @param {Roo.XComponent} c the top level component.
52214          */
52215         'buildcomplete' : true
52216         
52217     });
52218     this.region = this.region || 'center'; // default..
52219     Roo.XComponent.register(this);
52220     this.modules = false;
52221     this.el = false; // where the layout goes..
52222     
52223     
52224 }
52225 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52226     /**
52227      * @property el
52228      * The created element (with Roo.factory())
52229      * @type {Roo.Layout}
52230      */
52231     el  : false,
52232     
52233     /**
52234      * @property el
52235      * for BC  - use el in new code
52236      * @type {Roo.Layout}
52237      */
52238     panel : false,
52239     
52240     /**
52241      * @property layout
52242      * for BC  - use el in new code
52243      * @type {Roo.Layout}
52244      */
52245     layout : false,
52246     
52247      /**
52248      * @cfg {Function|boolean} disabled
52249      * If this module is disabled by some rule, return true from the funtion
52250      */
52251     disabled : false,
52252     
52253     /**
52254      * @cfg {String} parent 
52255      * Name of parent element which it get xtype added to..
52256      */
52257     parent: false,
52258     
52259     /**
52260      * @cfg {String} order
52261      * Used to set the order in which elements are created (usefull for multiple tabs)
52262      */
52263     
52264     order : false,
52265     /**
52266      * @cfg {String} name
52267      * String to display while loading.
52268      */
52269     name : false,
52270     /**
52271      * @cfg {String} region
52272      * Region to render component to (defaults to center)
52273      */
52274     region : 'center',
52275     
52276     /**
52277      * @cfg {Array} items
52278      * A single item array - the first element is the root of the tree..
52279      * It's done this way to stay compatible with the Xtype system...
52280      */
52281     items : false,
52282     
52283     
52284      /**
52285      * render
52286      * render element to dom or tree
52287      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52288      */
52289     
52290     render : function(el)
52291     {
52292         
52293         el = el || false;
52294         var hp = this.parent ? 1 : 0;
52295         
52296         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52297             // if parent is a '#.....' string, then let's use that..
52298             var ename = this.parent.substr(1)
52299             this.parent = false;
52300             el = Roo.get(ename);
52301             if (!el) {
52302                 Roo.log("Warning - element can not be found :#" + ename );
52303                 return;
52304             }
52305         }
52306         
52307         
52308         if (!this.parent) {
52309             
52310             el = el ? Roo.get(el) : false;
52311             
52312             // it's a top level one..
52313             this.parent =  {
52314                 el : new Roo.BorderLayout(el || document.body, {
52315                 
52316                      center: {
52317                          titlebar: false,
52318                          autoScroll:false,
52319                          closeOnTab: true,
52320                          tabPosition: 'top',
52321                           //resizeTabs: true,
52322                          alwaysShowTabs: el && hp? false :  true,
52323                          hideTabs: el || !hp ? true :  false,
52324                          minTabWidth: 140
52325                      }
52326                  })
52327             }
52328         }
52329         
52330         
52331             
52332         var tree = this.tree();
52333         tree.region = tree.region || this.region;
52334         this.el = this.parent.el.addxtype(tree);
52335         this.fireEvent('built', this);
52336         
52337         this.panel = this.el;
52338         this.layout = this.panel.layout;    
52339          
52340     }
52341     
52342 });
52343
52344 Roo.apply(Roo.XComponent, {
52345     
52346     /**
52347      * @property  buildCompleted
52348      * True when the builder has completed building the interface.
52349      * @type Boolean
52350      */
52351     buildCompleted : false,
52352      
52353     /**
52354      * @property  topModule
52355      * the upper most module - uses document.element as it's constructor.
52356      * @type Object
52357      */
52358      
52359     topModule  : false,
52360       
52361     /**
52362      * @property  modules
52363      * array of modules to be created by registration system.
52364      * @type {Array} of Roo.XComponent
52365      */
52366     
52367     modules : [],
52368     /**
52369      * @property  elmodules
52370      * array of modules to be created by which use #ID 
52371      * @type {Array} of Roo.XComponent
52372      */
52373      
52374     elmodules : [],
52375
52376     
52377     /**
52378      * Register components to be built later.
52379      *
52380      * This solves the following issues
52381      * - Building is not done on page load, but after an authentication process has occured.
52382      * - Interface elements are registered on page load
52383      * - Parent Interface elements may not be loaded before child, so this handles that..
52384      * 
52385      *
52386      * example:
52387      * 
52388      * MyApp.register({
52389           order : '000001',
52390           module : 'Pman.Tab.projectMgr',
52391           region : 'center',
52392           parent : 'Pman.layout',
52393           disabled : false,  // or use a function..
52394         })
52395      
52396      * * @param {Object} details about module
52397      */
52398     register : function(obj) {
52399         this.modules.push(obj);
52400          
52401     },
52402     /**
52403      * convert a string to an object..
52404      * eg. 'AAA.BBB' -> finds AAA.BBB
52405
52406      */
52407     
52408     toObject : function(str)
52409     {
52410         if (!str || typeof(str) == 'object') {
52411             return str;
52412         }
52413         if (str.substring(0,1) == '#') {
52414             return str;
52415         }
52416
52417         var ar = str.split('.');
52418         var rt, o;
52419         rt = ar.shift();
52420             /** eval:var:o */
52421         try {
52422             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52423         } catch (e) {
52424             throw "Module not found : " + str;
52425         }
52426         
52427         if (o === false) {
52428             throw "Module not found : " + str;
52429         }
52430         Roo.each(ar, function(e) {
52431             if (typeof(o[e]) == 'undefined') {
52432                 throw "Module not found : " + str;
52433             }
52434             o = o[e];
52435         });
52436         
52437         return o;
52438         
52439     },
52440     
52441     
52442     /**
52443      * move modules into their correct place in the tree..
52444      * 
52445      */
52446     preBuild : function ()
52447     {
52448         var _t = this;
52449         Roo.each(this.modules , function (obj)
52450         {
52451             var opar = obj.parent;
52452             try { 
52453                 obj.parent = this.toObject(opar);
52454             } catch(e) {
52455                 Roo.log(e.toString());
52456                 return;
52457             }
52458             
52459             if (!obj.parent) {
52460                 this.topModule = obj;
52461                 return;
52462             }
52463             if (typeof(obj.parent) == 'string') {
52464                 this.elmodules.push(obj);
52465                 return;
52466             }
52467             if (obj.parent.constructor != Roo.XComponent) {
52468                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52469             }
52470             if (!obj.parent.modules) {
52471                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52472                     function(o) { return o.order + '' }
52473                 );
52474             }
52475             
52476             obj.parent.modules.add(obj);
52477         }, this);
52478     },
52479     
52480      /**
52481      * make a list of modules to build.
52482      * @return {Array} list of modules. 
52483      */ 
52484     
52485     buildOrder : function()
52486     {
52487         var _this = this;
52488         var cmp = function(a,b) {   
52489             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52490         };
52491         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52492             throw "No top level modules to build";
52493         }
52494         
52495         // make a flat list in order of modules to build.
52496         var mods = this.topModule ? [ this.topModule ] : [];
52497         Roo.each(this.elmodules,function(e) { mods.push(e) });
52498
52499         
52500         // add modules to their parents..
52501         var addMod = function(m) {
52502            // Roo.debug && Roo.log(m.modKey);
52503             
52504             mods.push(m);
52505             if (m.modules) {
52506                 m.modules.keySort('ASC',  cmp );
52507                 m.modules.each(addMod);
52508             }
52509             // not sure if this is used any more..
52510             if (m.finalize) {
52511                 m.finalize.name = m.name + " (clean up) ";
52512                 mods.push(m.finalize);
52513             }
52514             
52515         }
52516         if (this.topModule) { 
52517             this.topModule.modules.keySort('ASC',  cmp );
52518             this.topModule.modules.each(addMod);
52519         }
52520         return mods;
52521     },
52522     
52523      /**
52524      * Build the registered modules.
52525      * @param {Object} parent element.
52526      * @param {Function} optional method to call after module has been added.
52527      * 
52528      */ 
52529    
52530     build : function() 
52531     {
52532         
52533         this.preBuild();
52534         var mods = this.buildOrder();
52535       
52536         //this.allmods = mods;
52537         //Roo.debug && Roo.log(mods);
52538         //return;
52539         if (!mods.length) { // should not happen
52540             throw "NO modules!!!";
52541         }
52542         
52543         
52544         
52545         // flash it up as modal - so we store the mask!?
52546         Roo.MessageBox.show({ title: 'loading' });
52547         Roo.MessageBox.show({
52548            title: "Please wait...",
52549            msg: "Building Interface...",
52550            width:450,
52551            progress:true,
52552            closable:false,
52553            modal: false
52554           
52555         });
52556         var total = mods.length;
52557         
52558         var _this = this;
52559         var progressRun = function() {
52560             if (!mods.length) {
52561                 Roo.debug && Roo.log('hide?');
52562                 Roo.MessageBox.hide();
52563                 if (_this.topModule) { 
52564                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52565                 }
52566                 // THE END...
52567                 return false;   
52568             }
52569             
52570             var m = mods.shift();
52571             
52572             
52573             Roo.debug && Roo.log(m);
52574             // not sure if this is supported any more.. - modules that are are just function
52575             if (typeof(m) == 'function') { 
52576                 m.call(this);
52577                 return progressRun.defer(10, _this);
52578             } 
52579             
52580             
52581             
52582             Roo.MessageBox.updateProgress(
52583                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52584                     " of " + total + 
52585                     (m.name ? (' - ' + m.name) : '')
52586                     );
52587             
52588          
52589             // is the module disabled?
52590             var disabled = (typeof(m.disabled) == 'function') ?
52591                 m.disabled.call(m.module.disabled) : m.disabled;    
52592             
52593             
52594             if (disabled) {
52595                 return progressRun(); // we do not update the display!
52596             }
52597             
52598             // now build 
52599             
52600             m.render();
52601             // it's 10 on top level, and 1 on others??? why...
52602             return progressRun.defer(10, _this);
52603              
52604         }
52605         progressRun.defer(1, _this);
52606      
52607         
52608         
52609     }
52610     
52611      
52612    
52613     
52614     
52615 });
52616  //<script type="text/javascript">
52617
52618
52619 /**
52620  * @class Roo.Login
52621  * @extends Roo.LayoutDialog
52622  * A generic Login Dialog..... - only one needed in theory!?!?
52623  *
52624  * Fires XComponent builder on success...
52625  * 
52626  * Sends 
52627  *    username,password, lang = for login actions.
52628  *    check = 1 for periodic checking that sesion is valid.
52629  *    passwordRequest = email request password
52630  *    logout = 1 = to logout
52631  * 
52632  * Affects: (this id="????" elements)
52633  *   loading  (removed) (used to indicate application is loading)
52634  *   loading-mask (hides) (used to hide application when it's building loading)
52635  *   
52636  * 
52637  * Usage: 
52638  *    
52639  * 
52640  * Myapp.login = Roo.Login({
52641      url: xxxx,
52642    
52643      realm : 'Myapp', 
52644      
52645      
52646      method : 'POST',
52647      
52648      
52649      * 
52650  })
52651  * 
52652  * 
52653  * 
52654  **/
52655  
52656 Roo.Login = function(cfg)
52657 {
52658     this.addEvents({
52659         'refreshed' : true
52660     });
52661     
52662     Roo.apply(this,cfg);
52663     
52664     Roo.onReady(function() {
52665         this.onLoad();
52666     }, this);
52667     // call parent..
52668     
52669    
52670     Roo.Login.superclass.constructor.call(this, this);
52671     //this.addxtype(this.items[0]);
52672     
52673     
52674 }
52675
52676
52677 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52678     
52679     /**
52680      * @cfg {String} method
52681      * Method used to query for login details.
52682      */
52683     
52684     method : 'POST',
52685     /**
52686      * @cfg {String} url
52687      * URL to query login data. - eg. baseURL + '/Login.php'
52688      */
52689     url : '',
52690     
52691     /**
52692      * @property user
52693      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52694      * @type {Object} 
52695      */
52696     user : false,
52697     /**
52698      * @property checkFails
52699      * Number of times we have attempted to get authentication check, and failed.
52700      * @type {Number} 
52701      */
52702     checkFails : 0,
52703       /**
52704      * @property intervalID
52705      * The window interval that does the constant login checking.
52706      * @type {Number} 
52707      */
52708     intervalID : 0,
52709     
52710     
52711     onLoad : function() // called on page load...
52712     {
52713         // load 
52714          
52715         if (Roo.get('loading')) { // clear any loading indicator..
52716             Roo.get('loading').remove();
52717         }
52718         
52719         //this.switchLang('en'); // set the language to english..
52720        
52721         this.check({
52722             success:  function(response, opts)  {  // check successfull...
52723             
52724                 var res = this.processResponse(response);
52725                 this.checkFails =0;
52726                 if (!res.success) { // error!
52727                     this.checkFails = 5;
52728                     //console.log('call failure');
52729                     return this.failure(response,opts);
52730                 }
52731                 
52732                 if (!res.data.id) { // id=0 == login failure.
52733                     return this.show();
52734                 }
52735                 
52736                               
52737                         //console.log(success);
52738                 this.fillAuth(res.data);   
52739                 this.checkFails =0;
52740                 Roo.XComponent.build();
52741             },
52742             failure : this.show
52743         });
52744         
52745     }, 
52746     
52747     
52748     check: function(cfg) // called every so often to refresh cookie etc..
52749     {
52750         if (cfg.again) { // could be undefined..
52751             this.checkFails++;
52752         } else {
52753             this.checkFails = 0;
52754         }
52755         var _this = this;
52756         if (this.sending) {
52757             if ( this.checkFails > 4) {
52758                 Roo.MessageBox.alert("Error",  
52759                     "Error getting authentication status. - try reloading, or wait a while", function() {
52760                         _this.sending = false;
52761                     }); 
52762                 return;
52763             }
52764             cfg.again = true;
52765             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52766             return;
52767         }
52768         this.sending = true;
52769         
52770         Roo.Ajax.request({  
52771             url: this.url,
52772             params: {
52773                 getAuthUser: true
52774             },  
52775             method: this.method,
52776             success:  cfg.success || this.success,
52777             failure : cfg.failure || this.failure,
52778             scope : this,
52779             callCfg : cfg
52780               
52781         });  
52782     }, 
52783     
52784     
52785     logout: function()
52786     {
52787         window.onbeforeunload = function() { }; // false does not work for IE..
52788         this.user = false;
52789         var _this = this;
52790         
52791         Roo.Ajax.request({  
52792             url: this.url,
52793             params: {
52794                 logout: 1
52795             },  
52796             method: 'GET',
52797             failure : function() {
52798                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52799                     document.location = document.location.toString() + '?ts=' + Math.random();
52800                 });
52801                 
52802             },
52803             success : function() {
52804                 _this.user = false;
52805                 this.checkFails =0;
52806                 // fixme..
52807                 document.location = document.location.toString() + '?ts=' + Math.random();
52808             }
52809               
52810               
52811         }); 
52812     },
52813     
52814     processResponse : function (response)
52815     {
52816         var res = '';
52817         try {
52818             res = Roo.decode(response.responseText);
52819             // oops...
52820             if (typeof(res) != 'object') {
52821                 res = { success : false, errorMsg : res, errors : true };
52822             }
52823             if (typeof(res.success) == 'undefined') {
52824                 res.success = false;
52825             }
52826             
52827         } catch(e) {
52828             res = { success : false,  errorMsg : response.responseText, errors : true };
52829         }
52830         return res;
52831     },
52832     
52833     success : function(response, opts)  // check successfull...
52834     {  
52835         this.sending = false;
52836         var res = this.processResponse(response);
52837         if (!res.success) {
52838             return this.failure(response, opts);
52839         }
52840         if (!res.data || !res.data.id) {
52841             return this.failure(response,opts);
52842         }
52843         //console.log(res);
52844         this.fillAuth(res.data);
52845         
52846         this.checkFails =0;
52847         
52848     },
52849     
52850     
52851     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52852     {
52853         this.authUser = -1;
52854         this.sending = false;
52855         var res = this.processResponse(response);
52856         //console.log(res);
52857         if ( this.checkFails > 2) {
52858         
52859             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52860                 "Error getting authentication status. - try reloading"); 
52861             return;
52862         }
52863         opts.callCfg.again = true;
52864         this.check.defer(1000, this, [ opts.callCfg ]);
52865         return;  
52866     },
52867     
52868     
52869     
52870     fillAuth: function(au) {
52871         this.startAuthCheck();
52872         this.authUserId = au.id;
52873         this.authUser = au;
52874         this.lastChecked = new Date();
52875         this.fireEvent('refreshed', au);
52876         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52877         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52878         au.lang = au.lang || 'en';
52879         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52880         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52881         this.switchLang(au.lang );
52882         
52883      
52884         // open system... - -on setyp..
52885         if (this.authUserId  < 0) {
52886             Roo.MessageBox.alert("Warning", 
52887                 "This is an open system - please set up a admin user with a password.");  
52888         }
52889          
52890         //Pman.onload(); // which should do nothing if it's a re-auth result...
52891         
52892              
52893     },
52894     
52895     startAuthCheck : function() // starter for timeout checking..
52896     {
52897         if (this.intervalID) { // timer already in place...
52898             return false;
52899         }
52900         var _this = this;
52901         this.intervalID =  window.setInterval(function() {
52902               _this.check(false);
52903             }, 120000); // every 120 secs = 2mins..
52904         
52905         
52906     },
52907          
52908     
52909     switchLang : function (lang) 
52910     {
52911         _T = typeof(_T) == 'undefined' ? false : _T;
52912           if (!_T || !lang.length) {
52913             return;
52914         }
52915         
52916         if (!_T && lang != 'en') {
52917             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52918             return;
52919         }
52920         
52921         if (typeof(_T.en) == 'undefined') {
52922             _T.en = {};
52923             Roo.apply(_T.en, _T);
52924         }
52925         
52926         if (typeof(_T[lang]) == 'undefined') {
52927             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52928             return;
52929         }
52930         
52931         
52932         Roo.apply(_T, _T[lang]);
52933         // just need to set the text values for everything...
52934         var _this = this;
52935         /* this will not work ...
52936         if (this.form) { 
52937             
52938                
52939             function formLabel(name, val) {
52940                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52941             }
52942             
52943             formLabel('password', "Password"+':');
52944             formLabel('username', "Email Address"+':');
52945             formLabel('lang', "Language"+':');
52946             this.dialog.setTitle("Login");
52947             this.dialog.buttons[0].setText("Forgot Password");
52948             this.dialog.buttons[1].setText("Login");
52949         }
52950         */
52951         
52952         
52953     },
52954     
52955     
52956     title: "Login",
52957     modal: true,
52958     width:  350,
52959     //height: 230,
52960     height: 180,
52961     shadow: true,
52962     minWidth:200,
52963     minHeight:180,
52964     //proxyDrag: true,
52965     closable: false,
52966     draggable: false,
52967     collapsible: false,
52968     resizable: false,
52969     center: {  // needed??
52970         autoScroll:false,
52971         titlebar: false,
52972        // tabPosition: 'top',
52973         hideTabs: true,
52974         closeOnTab: true,
52975         alwaysShowTabs: false
52976     } ,
52977     listeners : {
52978         
52979         show  : function(dlg)
52980         {
52981             //console.log(this);
52982             this.form = this.layout.getRegion('center').activePanel.form;
52983             this.form.dialog = dlg;
52984             this.buttons[0].form = this.form;
52985             this.buttons[0].dialog = dlg;
52986             this.buttons[1].form = this.form;
52987             this.buttons[1].dialog = dlg;
52988            
52989            //this.resizeToLogo.defer(1000,this);
52990             // this is all related to resizing for logos..
52991             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52992            //// if (!sz) {
52993              //   this.resizeToLogo.defer(1000,this);
52994              //   return;
52995            // }
52996             //var w = Ext.lib.Dom.getViewWidth() - 100;
52997             //var h = Ext.lib.Dom.getViewHeight() - 100;
52998             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52999             //this.center();
53000             if (this.disabled) {
53001                 this.hide();
53002                 return;
53003             }
53004             
53005             if (this.user.id < 0) { // used for inital setup situations.
53006                 return;
53007             }
53008             
53009             if (this.intervalID) {
53010                 // remove the timer
53011                 window.clearInterval(this.intervalID);
53012                 this.intervalID = false;
53013             }
53014             
53015             
53016             if (Roo.get('loading')) {
53017                 Roo.get('loading').remove();
53018             }
53019             if (Roo.get('loading-mask')) {
53020                 Roo.get('loading-mask').hide();
53021             }
53022             
53023             //incomming._node = tnode;
53024             this.form.reset();
53025             //this.dialog.modal = !modal;
53026             //this.dialog.show();
53027             this.el.unmask(); 
53028             
53029             
53030             this.form.setValues({
53031                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53032                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53033             });
53034             
53035             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53036             if (this.form.findField('username').getValue().length > 0 ){
53037                 this.form.findField('password').focus();
53038             } else {
53039                this.form.findField('username').focus();
53040             }
53041     
53042         }
53043     },
53044     items : [
53045          {
53046        
53047             xtype : 'ContentPanel',
53048             xns : Roo,
53049             region: 'center',
53050             fitToFrame : true,
53051             
53052             items : [
53053     
53054                 {
53055                
53056                     xtype : 'Form',
53057                     xns : Roo.form,
53058                     labelWidth: 100,
53059                     style : 'margin: 10px;',
53060                     
53061                     listeners : {
53062                         actionfailed : function(f, act) {
53063                             // form can return { errors: .... }
53064                                 
53065                             //act.result.errors // invalid form element list...
53066                             //act.result.errorMsg// invalid form element list...
53067                             
53068                             this.dialog.el.unmask();
53069                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53070                                         "Login failed - communication error - try again.");
53071                                       
53072                         },
53073                         actioncomplete: function(re, act) {
53074                              
53075                             Roo.state.Manager.set(
53076                                 this.dialog.realm + '.username',  
53077                                     this.findField('username').getValue()
53078                             );
53079                             Roo.state.Manager.set(
53080                                 this.dialog.realm + '.lang',  
53081                                 this.findField('lang').getValue() 
53082                             );
53083                             
53084                             this.dialog.fillAuth(act.result.data);
53085                               
53086                             this.dialog.hide();
53087                             
53088                             if (Roo.get('loading-mask')) {
53089                                 Roo.get('loading-mask').show();
53090                             }
53091                             Roo.XComponent.build();
53092                             
53093                              
53094                             
53095                         }
53096                     },
53097                     items : [
53098                         {
53099                             xtype : 'TextField',
53100                             xns : Roo.form,
53101                             fieldLabel: "Email Address",
53102                             name: 'username',
53103                             width:200,
53104                             autoCreate : {tag: "input", type: "text", size: "20"}
53105                         },
53106                         {
53107                             xtype : 'TextField',
53108                             xns : Roo.form,
53109                             fieldLabel: "Password",
53110                             inputType: 'password',
53111                             name: 'password',
53112                             width:200,
53113                             autoCreate : {tag: "input", type: "text", size: "20"},
53114                             listeners : {
53115                                 specialkey : function(e,ev) {
53116                                     if (ev.keyCode == 13) {
53117                                         this.form.dialog.el.mask("Logging in");
53118                                         this.form.doAction('submit', {
53119                                             url: this.form.dialog.url,
53120                                             method: this.form.dialog.method
53121                                         });
53122                                     }
53123                                 }
53124                             }  
53125                         },
53126                         {
53127                             xtype : 'ComboBox',
53128                             xns : Roo.form,
53129                             fieldLabel: "Language",
53130                             name : 'langdisp',
53131                             store: {
53132                                 xtype : 'SimpleStore',
53133                                 fields: ['lang', 'ldisp'],
53134                                 data : [
53135                                     [ 'en', 'English' ],
53136                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53137                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53138                                 ]
53139                             },
53140                             
53141                             valueField : 'lang',
53142                             hiddenName:  'lang',
53143                             width: 200,
53144                             displayField:'ldisp',
53145                             typeAhead: false,
53146                             editable: false,
53147                             mode: 'local',
53148                             triggerAction: 'all',
53149                             emptyText:'Select a Language...',
53150                             selectOnFocus:true,
53151                             listeners : {
53152                                 select :  function(cb, rec, ix) {
53153                                     this.form.switchLang(rec.data.lang);
53154                                 }
53155                             }
53156                         
53157                         }
53158                     ]
53159                 }
53160                   
53161                 
53162             ]
53163         }
53164     ],
53165     buttons : [
53166         {
53167             xtype : 'Button',
53168             xns : 'Roo',
53169             text : "Forgot Password",
53170             listeners : {
53171                 click : function() {
53172                     //console.log(this);
53173                     var n = this.form.findField('username').getValue();
53174                     if (!n.length) {
53175                         Roo.MessageBox.alert("Error", "Fill in your email address");
53176                         return;
53177                     }
53178                     Roo.Ajax.request({
53179                         url: this.dialog.url,
53180                         params: {
53181                             passwordRequest: n
53182                         },
53183                         method: this.dialog.method,
53184                         success:  function(response, opts)  {  // check successfull...
53185                         
53186                             var res = this.dialog.processResponse(response);
53187                             if (!res.success) { // error!
53188                                Roo.MessageBox.alert("Error" ,
53189                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53190                                return;
53191                             }
53192                             Roo.MessageBox.alert("Notice" ,
53193                                 "Please check you email for the Password Reset message");
53194                         },
53195                         failure : function() {
53196                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53197                         }
53198                         
53199                     });
53200                 }
53201             }
53202         },
53203         {
53204             xtype : 'Button',
53205             xns : 'Roo',
53206             text : "Login",
53207             listeners : {
53208                 
53209                 click : function () {
53210                         
53211                     this.dialog.el.mask("Logging in");
53212                     this.form.doAction('submit', {
53213                             url: this.dialog.url,
53214                             method: this.dialog.method
53215                     });
53216                 }
53217             }
53218         }
53219     ]
53220   
53221   
53222 })
53223  
53224
53225
53226