b8ee0efc591eb61a99d90889dad998ea32644553
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   T      CST        Timezone setting of the machine running the code
1011   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1012 </pre>
1013  *
1014  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1015  * <pre><code>
1016 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1017 document.write(dt.format('Y-m-d'));                         //2007-01-10
1018 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1019 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1020  </code></pre>
1021  *
1022  * Here are some standard date/time patterns that you might find helpful.  They
1023  * are not part of the source of Date.js, but to use them you can simply copy this
1024  * block of code into any script that is included after Date.js and they will also become
1025  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1026  * <pre><code>
1027 Date.patterns = {
1028     ISO8601Long:"Y-m-d H:i:s",
1029     ISO8601Short:"Y-m-d",
1030     ShortDate: "n/j/Y",
1031     LongDate: "l, F d, Y",
1032     FullDateTime: "l, F d, Y g:i:s A",
1033     MonthDay: "F d",
1034     ShortTime: "g:i A",
1035     LongTime: "g:i:s A",
1036     SortableDateTime: "Y-m-d\\TH:i:s",
1037     UniversalSortableDateTime: "Y-m-d H:i:sO",
1038     YearMonth: "F, Y"
1039 };
1040 </code></pre>
1041  *
1042  * Example usage:
1043  * <pre><code>
1044 var dt = new Date();
1045 document.write(dt.format(Date.patterns.ShortDate));
1046  </code></pre>
1047  */
1048
1049 /*
1050  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1051  * They generate precompiled functions from date formats instead of parsing and
1052  * processing the pattern every time you format a date.  These functions are available
1053  * on every Date object (any javascript function).
1054  *
1055  * The original article and download are here:
1056  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1057  *
1058  */
1059  
1060  
1061  // was in core
1062 /**
1063  Returns the number of milliseconds between this date and date
1064  @param {Date} date (optional) Defaults to now
1065  @return {Number} The diff in milliseconds
1066  @member Date getElapsed
1067  */
1068 Date.prototype.getElapsed = function(date) {
1069         return Math.abs((date || new Date()).getTime()-this.getTime());
1070 };
1071 // was in date file..
1072
1073
1074 // private
1075 Date.parseFunctions = {count:0};
1076 // private
1077 Date.parseRegexes = [];
1078 // private
1079 Date.formatFunctions = {count:0};
1080
1081 // private
1082 Date.prototype.dateFormat = function(format) {
1083     if (Date.formatFunctions[format] == null) {
1084         Date.createNewFormat(format);
1085     }
1086     var func = Date.formatFunctions[format];
1087     return this[func]();
1088 };
1089
1090
1091 /**
1092  * Formats a date given the supplied format string
1093  * @param {String} format The format string
1094  * @return {String} The formatted date
1095  * @method
1096  */
1097 Date.prototype.format = Date.prototype.dateFormat;
1098
1099 // private
1100 Date.createNewFormat = function(format) {
1101     var funcName = "format" + Date.formatFunctions.count++;
1102     Date.formatFunctions[format] = funcName;
1103     var code = "Date.prototype." + funcName + " = function(){return ";
1104     var special = false;
1105     var ch = '';
1106     for (var i = 0; i < format.length; ++i) {
1107         ch = format.charAt(i);
1108         if (!special && ch == "\\") {
1109             special = true;
1110         }
1111         else if (special) {
1112             special = false;
1113             code += "'" + String.escape(ch) + "' + ";
1114         }
1115         else {
1116             code += Date.getFormatCode(ch);
1117         }
1118     }
1119     /** eval:var:zzzzzzzzzzzzz */
1120     eval(code.substring(0, code.length - 3) + ";}");
1121 };
1122
1123 // private
1124 Date.getFormatCode = function(character) {
1125     switch (character) {
1126     case "d":
1127         return "String.leftPad(this.getDate(), 2, '0') + ";
1128     case "D":
1129         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1130     case "j":
1131         return "this.getDate() + ";
1132     case "l":
1133         return "Date.dayNames[this.getDay()] + ";
1134     case "S":
1135         return "this.getSuffix() + ";
1136     case "w":
1137         return "this.getDay() + ";
1138     case "z":
1139         return "this.getDayOfYear() + ";
1140     case "W":
1141         return "this.getWeekOfYear() + ";
1142     case "F":
1143         return "Date.monthNames[this.getMonth()] + ";
1144     case "m":
1145         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1146     case "M":
1147         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1148     case "n":
1149         return "(this.getMonth() + 1) + ";
1150     case "t":
1151         return "this.getDaysInMonth() + ";
1152     case "L":
1153         return "(this.isLeapYear() ? 1 : 0) + ";
1154     case "Y":
1155         return "this.getFullYear() + ";
1156     case "y":
1157         return "('' + this.getFullYear()).substring(2, 4) + ";
1158     case "a":
1159         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1160     case "A":
1161         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1162     case "g":
1163         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1164     case "G":
1165         return "this.getHours() + ";
1166     case "h":
1167         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1168     case "H":
1169         return "String.leftPad(this.getHours(), 2, '0') + ";
1170     case "i":
1171         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1172     case "s":
1173         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1174     case "O":
1175         return "this.getGMTOffset() + ";
1176     case "T":
1177         return "this.getTimezone() + ";
1178     case "Z":
1179         return "(this.getTimezoneOffset() * -60) + ";
1180     default:
1181         return "'" + String.escape(character) + "' + ";
1182     }
1183 };
1184
1185 /**
1186  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1187  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1188  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1189  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1190  * string or the parse operation will fail.
1191  * Example Usage:
1192 <pre><code>
1193 //dt = Fri May 25 2007 (current date)
1194 var dt = new Date();
1195
1196 //dt = Thu May 25 2006 (today's month/day in 2006)
1197 dt = Date.parseDate("2006", "Y");
1198
1199 //dt = Sun Jan 15 2006 (all date parts specified)
1200 dt = Date.parseDate("2006-1-15", "Y-m-d");
1201
1202 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1203 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1204 </code></pre>
1205  * @param {String} input The unparsed date as a string
1206  * @param {String} format The format the date is in
1207  * @return {Date} The parsed date
1208  * @static
1209  */
1210 Date.parseDate = function(input, format) {
1211     if (Date.parseFunctions[format] == null) {
1212         Date.createParser(format);
1213     }
1214     var func = Date.parseFunctions[format];
1215     return Date[func](input);
1216 };
1217 /**
1218  * @private
1219  */
1220 Date.createParser = function(format) {
1221     var funcName = "parse" + Date.parseFunctions.count++;
1222     var regexNum = Date.parseRegexes.length;
1223     var currentGroup = 1;
1224     Date.parseFunctions[format] = funcName;
1225
1226     var code = "Date." + funcName + " = function(input){\n"
1227         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1228         + "var d = new Date();\n"
1229         + "y = d.getFullYear();\n"
1230         + "m = d.getMonth();\n"
1231         + "d = d.getDate();\n"
1232         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1233         + "if (results && results.length > 0) {";
1234     var regex = "";
1235
1236     var special = false;
1237     var ch = '';
1238     for (var i = 0; i < format.length; ++i) {
1239         ch = format.charAt(i);
1240         if (!special && ch == "\\") {
1241             special = true;
1242         }
1243         else if (special) {
1244             special = false;
1245             regex += String.escape(ch);
1246         }
1247         else {
1248             var obj = Date.formatCodeToRegex(ch, currentGroup);
1249             currentGroup += obj.g;
1250             regex += obj.s;
1251             if (obj.g && obj.c) {
1252                 code += obj.c;
1253             }
1254         }
1255     }
1256
1257     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1258         + "{v = new Date(y, m, d, h, i, s);}\n"
1259         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1260         + "{v = new Date(y, m, d, h, i);}\n"
1261         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1262         + "{v = new Date(y, m, d, h);}\n"
1263         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1264         + "{v = new Date(y, m, d);}\n"
1265         + "else if (y >= 0 && m >= 0)\n"
1266         + "{v = new Date(y, m);}\n"
1267         + "else if (y >= 0)\n"
1268         + "{v = new Date(y);}\n"
1269         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1270         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1271         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1272         + ";}";
1273
1274     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1275     /** eval:var:zzzzzzzzzzzzz */
1276     eval(code);
1277 };
1278
1279 // private
1280 Date.formatCodeToRegex = function(character, currentGroup) {
1281     switch (character) {
1282     case "D":
1283         return {g:0,
1284         c:null,
1285         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1286     case "j":
1287         return {g:1,
1288             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1289             s:"(\\d{1,2})"}; // day of month without leading zeroes
1290     case "d":
1291         return {g:1,
1292             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1293             s:"(\\d{2})"}; // day of month with leading zeroes
1294     case "l":
1295         return {g:0,
1296             c:null,
1297             s:"(?:" + Date.dayNames.join("|") + ")"};
1298     case "S":
1299         return {g:0,
1300             c:null,
1301             s:"(?:st|nd|rd|th)"};
1302     case "w":
1303         return {g:0,
1304             c:null,
1305             s:"\\d"};
1306     case "z":
1307         return {g:0,
1308             c:null,
1309             s:"(?:\\d{1,3})"};
1310     case "W":
1311         return {g:0,
1312             c:null,
1313             s:"(?:\\d{2})"};
1314     case "F":
1315         return {g:1,
1316             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1317             s:"(" + Date.monthNames.join("|") + ")"};
1318     case "M":
1319         return {g:1,
1320             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1321             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1322     case "n":
1323         return {g:1,
1324             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1325             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1326     case "m":
1327         return {g:1,
1328             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1329             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1330     case "t":
1331         return {g:0,
1332             c:null,
1333             s:"\\d{1,2}"};
1334     case "L":
1335         return {g:0,
1336             c:null,
1337             s:"(?:1|0)"};
1338     case "Y":
1339         return {g:1,
1340             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1341             s:"(\\d{4})"};
1342     case "y":
1343         return {g:1,
1344             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1345                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1346             s:"(\\d{1,2})"};
1347     case "a":
1348         return {g:1,
1349             c:"if (results[" + currentGroup + "] == 'am') {\n"
1350                 + "if (h == 12) { h = 0; }\n"
1351                 + "} else { if (h < 12) { h += 12; }}",
1352             s:"(am|pm)"};
1353     case "A":
1354         return {g:1,
1355             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1356                 + "if (h == 12) { h = 0; }\n"
1357                 + "} else { if (h < 12) { h += 12; }}",
1358             s:"(AM|PM)"};
1359     case "g":
1360     case "G":
1361         return {g:1,
1362             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1363             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1364     case "h":
1365     case "H":
1366         return {g:1,
1367             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1368             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1369     case "i":
1370         return {g:1,
1371             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{2})"};
1373     case "s":
1374         return {g:1,
1375             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"};
1377     case "O":
1378         return {g:1,
1379             c:[
1380                 "o = results[", currentGroup, "];\n",
1381                 "var sn = o.substring(0,1);\n", // get + / - sign
1382                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1383                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1384                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1385                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1386             ].join(""),
1387             s:"([+\-]\\d{4})"};
1388     case "T":
1389         return {g:0,
1390             c:null,
1391             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1392     case "Z":
1393         return {g:1,
1394             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1395                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1396             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1397     default:
1398         return {g:0,
1399             c:null,
1400             s:String.escape(character)};
1401     }
1402 };
1403
1404 /**
1405  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1406  * @return {String} The abbreviated timezone name (e.g. 'CST')
1407  */
1408 Date.prototype.getTimezone = function() {
1409     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1410 };
1411
1412 /**
1413  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1414  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1415  */
1416 Date.prototype.getGMTOffset = function() {
1417     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1418         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1419         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1420 };
1421
1422 /**
1423  * Get the numeric day number of the year, adjusted for leap year.
1424  * @return {Number} 0 through 364 (365 in leap years)
1425  */
1426 Date.prototype.getDayOfYear = function() {
1427     var num = 0;
1428     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1429     for (var i = 0; i < this.getMonth(); ++i) {
1430         num += Date.daysInMonth[i];
1431     }
1432     return num + this.getDate() - 1;
1433 };
1434
1435 /**
1436  * Get the string representation of the numeric week number of the year
1437  * (equivalent to the format specifier 'W').
1438  * @return {String} '00' through '52'
1439  */
1440 Date.prototype.getWeekOfYear = function() {
1441     // Skip to Thursday of this week
1442     var now = this.getDayOfYear() + (4 - this.getDay());
1443     // Find the first Thursday of the year
1444     var jan1 = new Date(this.getFullYear(), 0, 1);
1445     var then = (7 - jan1.getDay() + 4);
1446     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1447 };
1448
1449 /**
1450  * Whether or not the current date is in a leap year.
1451  * @return {Boolean} True if the current date is in a leap year, else false
1452  */
1453 Date.prototype.isLeapYear = function() {
1454     var year = this.getFullYear();
1455     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1456 };
1457
1458 /**
1459  * Get the first day of the current month, adjusted for leap year.  The returned value
1460  * is the numeric day index within the week (0-6) which can be used in conjunction with
1461  * the {@link #monthNames} array to retrieve the textual day name.
1462  * Example:
1463  *<pre><code>
1464 var dt = new Date('1/10/2007');
1465 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1466 </code></pre>
1467  * @return {Number} The day number (0-6)
1468  */
1469 Date.prototype.getFirstDayOfMonth = function() {
1470     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1471     return (day < 0) ? (day + 7) : day;
1472 };
1473
1474 /**
1475  * Get the last day of the current month, adjusted for leap year.  The returned value
1476  * is the numeric day index within the week (0-6) which can be used in conjunction with
1477  * the {@link #monthNames} array to retrieve the textual day name.
1478  * Example:
1479  *<pre><code>
1480 var dt = new Date('1/10/2007');
1481 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1482 </code></pre>
1483  * @return {Number} The day number (0-6)
1484  */
1485 Date.prototype.getLastDayOfMonth = function() {
1486     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1487     return (day < 0) ? (day + 7) : day;
1488 };
1489
1490
1491 /**
1492  * Get the first date of this date's month
1493  * @return {Date}
1494  */
1495 Date.prototype.getFirstDateOfMonth = function() {
1496     return new Date(this.getFullYear(), this.getMonth(), 1);
1497 };
1498
1499 /**
1500  * Get the last date of this date's month
1501  * @return {Date}
1502  */
1503 Date.prototype.getLastDateOfMonth = function() {
1504     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1505 };
1506 /**
1507  * Get the number of days in the current month, adjusted for leap year.
1508  * @return {Number} The number of days in the month
1509  */
1510 Date.prototype.getDaysInMonth = function() {
1511     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1512     return Date.daysInMonth[this.getMonth()];
1513 };
1514
1515 /**
1516  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1517  * @return {String} 'st, 'nd', 'rd' or 'th'
1518  */
1519 Date.prototype.getSuffix = function() {
1520     switch (this.getDate()) {
1521         case 1:
1522         case 21:
1523         case 31:
1524             return "st";
1525         case 2:
1526         case 22:
1527             return "nd";
1528         case 3:
1529         case 23:
1530             return "rd";
1531         default:
1532             return "th";
1533     }
1534 };
1535
1536 // private
1537 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1538
1539 /**
1540  * An array of textual month names.
1541  * Override these values for international dates, for example...
1542  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1543  * @type Array
1544  * @static
1545  */
1546 Date.monthNames =
1547    ["January",
1548     "February",
1549     "March",
1550     "April",
1551     "May",
1552     "June",
1553     "July",
1554     "August",
1555     "September",
1556     "October",
1557     "November",
1558     "December"];
1559
1560 /**
1561  * An array of textual day names.
1562  * Override these values for international dates, for example...
1563  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1564  * @type Array
1565  * @static
1566  */
1567 Date.dayNames =
1568    ["Sunday",
1569     "Monday",
1570     "Tuesday",
1571     "Wednesday",
1572     "Thursday",
1573     "Friday",
1574     "Saturday"];
1575
1576 // private
1577 Date.y2kYear = 50;
1578 // private
1579 Date.monthNumbers = {
1580     Jan:0,
1581     Feb:1,
1582     Mar:2,
1583     Apr:3,
1584     May:4,
1585     Jun:5,
1586     Jul:6,
1587     Aug:7,
1588     Sep:8,
1589     Oct:9,
1590     Nov:10,
1591     Dec:11};
1592
1593 /**
1594  * Creates and returns a new Date instance with the exact same date value as the called instance.
1595  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1596  * variable will also be changed.  When the intention is to create a new variable that will not
1597  * modify the original instance, you should create a clone.
1598  *
1599  * Example of correctly cloning a date:
1600  * <pre><code>
1601 //wrong way:
1602 var orig = new Date('10/1/2006');
1603 var copy = orig;
1604 copy.setDate(5);
1605 document.write(orig);  //returns 'Thu Oct 05 2006'!
1606
1607 //correct way:
1608 var orig = new Date('10/1/2006');
1609 var copy = orig.clone();
1610 copy.setDate(5);
1611 document.write(orig);  //returns 'Thu Oct 01 2006'
1612 </code></pre>
1613  * @return {Date} The new Date instance
1614  */
1615 Date.prototype.clone = function() {
1616         return new Date(this.getTime());
1617 };
1618
1619 /**
1620  * Clears any time information from this date
1621  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1622  @return {Date} this or the clone
1623  */
1624 Date.prototype.clearTime = function(clone){
1625     if(clone){
1626         return this.clone().clearTime();
1627     }
1628     this.setHours(0);
1629     this.setMinutes(0);
1630     this.setSeconds(0);
1631     this.setMilliseconds(0);
1632     return this;
1633 };
1634
1635 // private
1636 // safari setMonth is broken
1637 if(Roo.isSafari){
1638     Date.brokenSetMonth = Date.prototype.setMonth;
1639         Date.prototype.setMonth = function(num){
1640                 if(num <= -1){
1641                         var n = Math.ceil(-num);
1642                         var back_year = Math.ceil(n/12);
1643                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1644                         this.setFullYear(this.getFullYear() - back_year);
1645                         return Date.brokenSetMonth.call(this, month);
1646                 } else {
1647                         return Date.brokenSetMonth.apply(this, arguments);
1648                 }
1649         };
1650 }
1651
1652 /** Date interval constant 
1653 * @static 
1654 * @type String */
1655 Date.MILLI = "ms";
1656 /** Date interval constant 
1657 * @static 
1658 * @type String */
1659 Date.SECOND = "s";
1660 /** Date interval constant 
1661 * @static 
1662 * @type String */
1663 Date.MINUTE = "mi";
1664 /** Date interval constant 
1665 * @static 
1666 * @type String */
1667 Date.HOUR = "h";
1668 /** Date interval constant 
1669 * @static 
1670 * @type String */
1671 Date.DAY = "d";
1672 /** Date interval constant 
1673 * @static 
1674 * @type String */
1675 Date.MONTH = "mo";
1676 /** Date interval constant 
1677 * @static 
1678 * @type String */
1679 Date.YEAR = "y";
1680
1681 /**
1682  * Provides a convenient method of performing basic date arithmetic.  This method
1683  * does not modify the Date instance being called - it creates and returns
1684  * a new Date instance containing the resulting date value.
1685  *
1686  * Examples:
1687  * <pre><code>
1688 //Basic usage:
1689 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1690 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1691
1692 //Negative values will subtract correctly:
1693 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1694 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1695
1696 //You can even chain several calls together in one line!
1697 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1698 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1699  </code></pre>
1700  *
1701  * @param {String} interval   A valid date interval enum value
1702  * @param {Number} value      The amount to add to the current date
1703  * @return {Date} The new Date instance
1704  */
1705 Date.prototype.add = function(interval, value){
1706   var d = this.clone();
1707   if (!interval || value === 0) return d;
1708   switch(interval.toLowerCase()){
1709     case Date.MILLI:
1710       d.setMilliseconds(this.getMilliseconds() + value);
1711       break;
1712     case Date.SECOND:
1713       d.setSeconds(this.getSeconds() + value);
1714       break;
1715     case Date.MINUTE:
1716       d.setMinutes(this.getMinutes() + value);
1717       break;
1718     case Date.HOUR:
1719       d.setHours(this.getHours() + value);
1720       break;
1721     case Date.DAY:
1722       d.setDate(this.getDate() + value);
1723       break;
1724     case Date.MONTH:
1725       var day = this.getDate();
1726       if(day > 28){
1727           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1728       }
1729       d.setDate(day);
1730       d.setMonth(this.getMonth() + value);
1731       break;
1732     case Date.YEAR:
1733       d.setFullYear(this.getFullYear() + value);
1734       break;
1735   }
1736   return d;
1737 };/*
1738  * Based on:
1739  * Ext JS Library 1.1.1
1740  * Copyright(c) 2006-2007, Ext JS, LLC.
1741  *
1742  * Originally Released Under LGPL - original licence link has changed is not relivant.
1743  *
1744  * Fork - LGPL
1745  * <script type="text/javascript">
1746  */
1747
1748 Roo.lib.Dom = {
1749     getViewWidth : function(full) {
1750         return full ? this.getDocumentWidth() : this.getViewportWidth();
1751     },
1752
1753     getViewHeight : function(full) {
1754         return full ? this.getDocumentHeight() : this.getViewportHeight();
1755     },
1756
1757     getDocumentHeight: function() {
1758         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1759         return Math.max(scrollHeight, this.getViewportHeight());
1760     },
1761
1762     getDocumentWidth: function() {
1763         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1764         return Math.max(scrollWidth, this.getViewportWidth());
1765     },
1766
1767     getViewportHeight: function() {
1768         var height = self.innerHeight;
1769         var mode = document.compatMode;
1770
1771         if ((mode || Roo.isIE) && !Roo.isOpera) {
1772             height = (mode == "CSS1Compat") ?
1773                      document.documentElement.clientHeight :
1774                      document.body.clientHeight;
1775         }
1776
1777         return height;
1778     },
1779
1780     getViewportWidth: function() {
1781         var width = self.innerWidth;
1782         var mode = document.compatMode;
1783
1784         if (mode || Roo.isIE) {
1785             width = (mode == "CSS1Compat") ?
1786                     document.documentElement.clientWidth :
1787                     document.body.clientWidth;
1788         }
1789         return width;
1790     },
1791
1792     isAncestor : function(p, c) {
1793         p = Roo.getDom(p);
1794         c = Roo.getDom(c);
1795         if (!p || !c) {
1796             return false;
1797         }
1798
1799         if (p.contains && !Roo.isSafari) {
1800             return p.contains(c);
1801         } else if (p.compareDocumentPosition) {
1802             return !!(p.compareDocumentPosition(c) & 16);
1803         } else {
1804             var parent = c.parentNode;
1805             while (parent) {
1806                 if (parent == p) {
1807                     return true;
1808                 }
1809                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1810                     return false;
1811                 }
1812                 parent = parent.parentNode;
1813             }
1814             return false;
1815         }
1816     },
1817
1818     getRegion : function(el) {
1819         return Roo.lib.Region.getRegion(el);
1820     },
1821
1822     getY : function(el) {
1823         return this.getXY(el)[1];
1824     },
1825
1826     getX : function(el) {
1827         return this.getXY(el)[0];
1828     },
1829
1830     getXY : function(el) {
1831         var p, pe, b, scroll, bd = document.body;
1832         el = Roo.getDom(el);
1833         var fly = Roo.lib.AnimBase.fly;
1834         if (el.getBoundingClientRect) {
1835             b = el.getBoundingClientRect();
1836             scroll = fly(document).getScroll();
1837             return [b.left + scroll.left, b.top + scroll.top];
1838         }
1839         var x = 0, y = 0;
1840
1841         p = el;
1842
1843         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1844
1845         while (p) {
1846
1847             x += p.offsetLeft;
1848             y += p.offsetTop;
1849
1850             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1851                 hasAbsolute = true;
1852             }
1853
1854             if (Roo.isGecko) {
1855                 pe = fly(p);
1856
1857                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1858                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1859
1860
1861                 x += bl;
1862                 y += bt;
1863
1864
1865                 if (p != el && pe.getStyle('overflow') != 'visible') {
1866                     x += bl;
1867                     y += bt;
1868                 }
1869             }
1870             p = p.offsetParent;
1871         }
1872
1873         if (Roo.isSafari && hasAbsolute) {
1874             x -= bd.offsetLeft;
1875             y -= bd.offsetTop;
1876         }
1877
1878         if (Roo.isGecko && !hasAbsolute) {
1879             var dbd = fly(bd);
1880             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1881             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1882         }
1883
1884         p = el.parentNode;
1885         while (p && p != bd) {
1886             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1887                 x -= p.scrollLeft;
1888                 y -= p.scrollTop;
1889             }
1890             p = p.parentNode;
1891         }
1892         return [x, y];
1893     },
1894  
1895   
1896
1897
1898     setXY : function(el, xy) {
1899         el = Roo.fly(el, '_setXY');
1900         el.position();
1901         var pts = el.translatePoints(xy);
1902         if (xy[0] !== false) {
1903             el.dom.style.left = pts.left + "px";
1904         }
1905         if (xy[1] !== false) {
1906             el.dom.style.top = pts.top + "px";
1907         }
1908     },
1909
1910     setX : function(el, x) {
1911         this.setXY(el, [x, false]);
1912     },
1913
1914     setY : function(el, y) {
1915         this.setXY(el, [false, y]);
1916     }
1917 };
1918 /*
1919  * Portions of this file are based on pieces of Yahoo User Interface Library
1920  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1921  * YUI licensed under the BSD License:
1922  * http://developer.yahoo.net/yui/license.txt
1923  * <script type="text/javascript">
1924  *
1925  */
1926
1927 Roo.lib.Event = function() {
1928     var loadComplete = false;
1929     var listeners = [];
1930     var unloadListeners = [];
1931     var retryCount = 0;
1932     var onAvailStack = [];
1933     var counter = 0;
1934     var lastError = null;
1935
1936     return {
1937         POLL_RETRYS: 200,
1938         POLL_INTERVAL: 20,
1939         EL: 0,
1940         TYPE: 1,
1941         FN: 2,
1942         WFN: 3,
1943         OBJ: 3,
1944         ADJ_SCOPE: 4,
1945         _interval: null,
1946
1947         startInterval: function() {
1948             if (!this._interval) {
1949                 var self = this;
1950                 var callback = function() {
1951                     self._tryPreloadAttach();
1952                 };
1953                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1954
1955             }
1956         },
1957
1958         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1959             onAvailStack.push({ id:         p_id,
1960                 fn:         p_fn,
1961                 obj:        p_obj,
1962                 override:   p_override,
1963                 checkReady: false    });
1964
1965             retryCount = this.POLL_RETRYS;
1966             this.startInterval();
1967         },
1968
1969
1970         addListener: function(el, eventName, fn) {
1971             el = Roo.getDom(el);
1972             if (!el || !fn) {
1973                 return false;
1974             }
1975
1976             if ("unload" == eventName) {
1977                 unloadListeners[unloadListeners.length] =
1978                 [el, eventName, fn];
1979                 return true;
1980             }
1981
1982             var wrappedFn = function(e) {
1983                 return fn(Roo.lib.Event.getEvent(e));
1984             };
1985
1986             var li = [el, eventName, fn, wrappedFn];
1987
1988             var index = listeners.length;
1989             listeners[index] = li;
1990
1991             this.doAdd(el, eventName, wrappedFn, false);
1992             return true;
1993
1994         },
1995
1996
1997         removeListener: function(el, eventName, fn) {
1998             var i, len;
1999
2000             el = Roo.getDom(el);
2001
2002             if(!fn) {
2003                 return this.purgeElement(el, false, eventName);
2004             }
2005
2006
2007             if ("unload" == eventName) {
2008
2009                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2010                     var li = unloadListeners[i];
2011                     if (li &&
2012                         li[0] == el &&
2013                         li[1] == eventName &&
2014                         li[2] == fn) {
2015                         unloadListeners.splice(i, 1);
2016                         return true;
2017                     }
2018                 }
2019
2020                 return false;
2021             }
2022
2023             var cacheItem = null;
2024
2025
2026             var index = arguments[3];
2027
2028             if ("undefined" == typeof index) {
2029                 index = this._getCacheIndex(el, eventName, fn);
2030             }
2031
2032             if (index >= 0) {
2033                 cacheItem = listeners[index];
2034             }
2035
2036             if (!el || !cacheItem) {
2037                 return false;
2038             }
2039
2040             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2041
2042             delete listeners[index][this.WFN];
2043             delete listeners[index][this.FN];
2044             listeners.splice(index, 1);
2045
2046             return true;
2047
2048         },
2049
2050
2051         getTarget: function(ev, resolveTextNode) {
2052             ev = ev.browserEvent || ev;
2053             var t = ev.target || ev.srcElement;
2054             return this.resolveTextNode(t);
2055         },
2056
2057
2058         resolveTextNode: function(node) {
2059             if (Roo.isSafari && node && 3 == node.nodeType) {
2060                 return node.parentNode;
2061             } else {
2062                 return node;
2063             }
2064         },
2065
2066
2067         getPageX: function(ev) {
2068             ev = ev.browserEvent || ev;
2069             var x = ev.pageX;
2070             if (!x && 0 !== x) {
2071                 x = ev.clientX || 0;
2072
2073                 if (Roo.isIE) {
2074                     x += this.getScroll()[1];
2075                 }
2076             }
2077
2078             return x;
2079         },
2080
2081
2082         getPageY: function(ev) {
2083             ev = ev.browserEvent || ev;
2084             var y = ev.pageY;
2085             if (!y && 0 !== y) {
2086                 y = ev.clientY || 0;
2087
2088                 if (Roo.isIE) {
2089                     y += this.getScroll()[0];
2090                 }
2091             }
2092
2093
2094             return y;
2095         },
2096
2097
2098         getXY: function(ev) {
2099             ev = ev.browserEvent || ev;
2100             return [this.getPageX(ev), this.getPageY(ev)];
2101         },
2102
2103
2104         getRelatedTarget: function(ev) {
2105             ev = ev.browserEvent || ev;
2106             var t = ev.relatedTarget;
2107             if (!t) {
2108                 if (ev.type == "mouseout") {
2109                     t = ev.toElement;
2110                 } else if (ev.type == "mouseover") {
2111                     t = ev.fromElement;
2112                 }
2113             }
2114
2115             return this.resolveTextNode(t);
2116         },
2117
2118
2119         getTime: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             if (!ev.time) {
2122                 var t = new Date().getTime();
2123                 try {
2124                     ev.time = t;
2125                 } catch(ex) {
2126                     this.lastError = ex;
2127                     return t;
2128                 }
2129             }
2130
2131             return ev.time;
2132         },
2133
2134
2135         stopEvent: function(ev) {
2136             this.stopPropagation(ev);
2137             this.preventDefault(ev);
2138         },
2139
2140
2141         stopPropagation: function(ev) {
2142             ev = ev.browserEvent || ev;
2143             if (ev.stopPropagation) {
2144                 ev.stopPropagation();
2145             } else {
2146                 ev.cancelBubble = true;
2147             }
2148         },
2149
2150
2151         preventDefault: function(ev) {
2152             ev = ev.browserEvent || ev;
2153             if(ev.preventDefault) {
2154                 ev.preventDefault();
2155             } else {
2156                 ev.returnValue = false;
2157             }
2158         },
2159
2160
2161         getEvent: function(e) {
2162             var ev = e || window.event;
2163             if (!ev) {
2164                 var c = this.getEvent.caller;
2165                 while (c) {
2166                     ev = c.arguments[0];
2167                     if (ev && Event == ev.constructor) {
2168                         break;
2169                     }
2170                     c = c.caller;
2171                 }
2172             }
2173             return ev;
2174         },
2175
2176
2177         getCharCode: function(ev) {
2178             ev = ev.browserEvent || ev;
2179             return ev.charCode || ev.keyCode || 0;
2180         },
2181
2182
2183         _getCacheIndex: function(el, eventName, fn) {
2184             for (var i = 0,len = listeners.length; i < len; ++i) {
2185                 var li = listeners[i];
2186                 if (li &&
2187                     li[this.FN] == fn &&
2188                     li[this.EL] == el &&
2189                     li[this.TYPE] == eventName) {
2190                     return i;
2191                 }
2192             }
2193
2194             return -1;
2195         },
2196
2197
2198         elCache: {},
2199
2200
2201         getEl: function(id) {
2202             return document.getElementById(id);
2203         },
2204
2205
2206         clearCache: function() {
2207         },
2208
2209
2210         _load: function(e) {
2211             loadComplete = true;
2212             var EU = Roo.lib.Event;
2213
2214
2215             if (Roo.isIE) {
2216                 EU.doRemove(window, "load", EU._load);
2217             }
2218         },
2219
2220
2221         _tryPreloadAttach: function() {
2222
2223             if (this.locked) {
2224                 return false;
2225             }
2226
2227             this.locked = true;
2228
2229
2230             var tryAgain = !loadComplete;
2231             if (!tryAgain) {
2232                 tryAgain = (retryCount > 0);
2233             }
2234
2235
2236             var notAvail = [];
2237             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2238                 var item = onAvailStack[i];
2239                 if (item) {
2240                     var el = this.getEl(item.id);
2241
2242                     if (el) {
2243                         if (!item.checkReady ||
2244                             loadComplete ||
2245                             el.nextSibling ||
2246                             (document && document.body)) {
2247
2248                             var scope = el;
2249                             if (item.override) {
2250                                 if (item.override === true) {
2251                                     scope = item.obj;
2252                                 } else {
2253                                     scope = item.override;
2254                                 }
2255                             }
2256                             item.fn.call(scope, item.obj);
2257                             onAvailStack[i] = null;
2258                         }
2259                     } else {
2260                         notAvail.push(item);
2261                     }
2262                 }
2263             }
2264
2265             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2266
2267             if (tryAgain) {
2268
2269                 this.startInterval();
2270             } else {
2271                 clearInterval(this._interval);
2272                 this._interval = null;
2273             }
2274
2275             this.locked = false;
2276
2277             return true;
2278
2279         },
2280
2281
2282         purgeElement: function(el, recurse, eventName) {
2283             var elListeners = this.getListeners(el, eventName);
2284             if (elListeners) {
2285                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2286                     var l = elListeners[i];
2287                     this.removeListener(el, l.type, l.fn);
2288                 }
2289             }
2290
2291             if (recurse && el && el.childNodes) {
2292                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2293                     this.purgeElement(el.childNodes[i], recurse, eventName);
2294                 }
2295             }
2296         },
2297
2298
2299         getListeners: function(el, eventName) {
2300             var results = [], searchLists;
2301             if (!eventName) {
2302                 searchLists = [listeners, unloadListeners];
2303             } else if (eventName == "unload") {
2304                 searchLists = [unloadListeners];
2305             } else {
2306                 searchLists = [listeners];
2307             }
2308
2309             for (var j = 0; j < searchLists.length; ++j) {
2310                 var searchList = searchLists[j];
2311                 if (searchList && searchList.length > 0) {
2312                     for (var i = 0,len = searchList.length; i < len; ++i) {
2313                         var l = searchList[i];
2314                         if (l && l[this.EL] === el &&
2315                             (!eventName || eventName === l[this.TYPE])) {
2316                             results.push({
2317                                 type:   l[this.TYPE],
2318                                 fn:     l[this.FN],
2319                                 obj:    l[this.OBJ],
2320                                 adjust: l[this.ADJ_SCOPE],
2321                                 index:  i
2322                             });
2323                         }
2324                     }
2325                 }
2326             }
2327
2328             return (results.length) ? results : null;
2329         },
2330
2331
2332         _unload: function(e) {
2333
2334             var EU = Roo.lib.Event, i, j, l, len, index;
2335
2336             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2337                 l = unloadListeners[i];
2338                 if (l) {
2339                     var scope = window;
2340                     if (l[EU.ADJ_SCOPE]) {
2341                         if (l[EU.ADJ_SCOPE] === true) {
2342                             scope = l[EU.OBJ];
2343                         } else {
2344                             scope = l[EU.ADJ_SCOPE];
2345                         }
2346                     }
2347                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2348                     unloadListeners[i] = null;
2349                     l = null;
2350                     scope = null;
2351                 }
2352             }
2353
2354             unloadListeners = null;
2355
2356             if (listeners && listeners.length > 0) {
2357                 j = listeners.length;
2358                 while (j) {
2359                     index = j - 1;
2360                     l = listeners[index];
2361                     if (l) {
2362                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2363                                 l[EU.FN], index);
2364                     }
2365                     j = j - 1;
2366                 }
2367                 l = null;
2368
2369                 EU.clearCache();
2370             }
2371
2372             EU.doRemove(window, "unload", EU._unload);
2373
2374         },
2375
2376
2377         getScroll: function() {
2378             var dd = document.documentElement, db = document.body;
2379             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2380                 return [dd.scrollTop, dd.scrollLeft];
2381             } else if (db) {
2382                 return [db.scrollTop, db.scrollLeft];
2383             } else {
2384                 return [0, 0];
2385             }
2386         },
2387
2388
2389         doAdd: function () {
2390             if (window.addEventListener) {
2391                 return function(el, eventName, fn, capture) {
2392                     el.addEventListener(eventName, fn, (capture));
2393                 };
2394             } else if (window.attachEvent) {
2395                 return function(el, eventName, fn, capture) {
2396                     el.attachEvent("on" + eventName, fn);
2397                 };
2398             } else {
2399                 return function() {
2400                 };
2401             }
2402         }(),
2403
2404
2405         doRemove: function() {
2406             if (window.removeEventListener) {
2407                 return function (el, eventName, fn, capture) {
2408                     el.removeEventListener(eventName, fn, (capture));
2409                 };
2410             } else if (window.detachEvent) {
2411                 return function (el, eventName, fn) {
2412                     el.detachEvent("on" + eventName, fn);
2413                 };
2414             } else {
2415                 return function() {
2416                 };
2417             }
2418         }()
2419     };
2420     
2421 }();
2422 (function() {     
2423    
2424     var E = Roo.lib.Event;
2425     E.on = E.addListener;
2426     E.un = E.removeListener;
2427
2428     if (document && document.body) {
2429         E._load();
2430     } else {
2431         E.doAdd(window, "load", E._load);
2432     }
2433     E.doAdd(window, "unload", E._unload);
2434     E._tryPreloadAttach();
2435 })();
2436
2437 /*
2438  * Portions of this file are based on pieces of Yahoo User Interface Library
2439  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2440  * YUI licensed under the BSD License:
2441  * http://developer.yahoo.net/yui/license.txt
2442  * <script type="text/javascript">
2443  *
2444  */
2445
2446 (function() {
2447     
2448     Roo.lib.Ajax = {
2449         request : function(method, uri, cb, data, options) {
2450             if(options){
2451                 var hs = options.headers;
2452                 if(hs){
2453                     for(var h in hs){
2454                         if(hs.hasOwnProperty(h)){
2455                             this.initHeader(h, hs[h], false);
2456                         }
2457                     }
2458                 }
2459                 if(options.xmlData){
2460                     this.initHeader('Content-Type', 'text/xml', false);
2461                     method = 'POST';
2462                     data = options.xmlData;
2463                 }
2464             }
2465
2466             return this.asyncRequest(method, uri, cb, data);
2467         },
2468
2469         serializeForm : function(form) {
2470             if(typeof form == 'string') {
2471                 form = (document.getElementById(form) || document.forms[form]);
2472             }
2473
2474             var el, name, val, disabled, data = '', hasSubmit = false;
2475             for (var i = 0; i < form.elements.length; i++) {
2476                 el = form.elements[i];
2477                 disabled = form.elements[i].disabled;
2478                 name = form.elements[i].name;
2479                 val = form.elements[i].value;
2480
2481                 if (!disabled && name){
2482                     switch (el.type)
2483                             {
2484                         case 'select-one':
2485                         case 'select-multiple':
2486                             for (var j = 0; j < el.options.length; j++) {
2487                                 if (el.options[j].selected) {
2488                                     if (Roo.isIE) {
2489                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2490                                     }
2491                                     else {
2492                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2493                                     }
2494                                 }
2495                             }
2496                             break;
2497                         case 'radio':
2498                         case 'checkbox':
2499                             if (el.checked) {
2500                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2501                             }
2502                             break;
2503                         case 'file':
2504
2505                         case undefined:
2506
2507                         case 'reset':
2508
2509                         case 'button':
2510
2511                             break;
2512                         case 'submit':
2513                             if(hasSubmit == false) {
2514                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2515                                 hasSubmit = true;
2516                             }
2517                             break;
2518                         default:
2519                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2520                             break;
2521                     }
2522                 }
2523             }
2524             data = data.substr(0, data.length - 1);
2525             return data;
2526         },
2527
2528         headers:{},
2529
2530         hasHeaders:false,
2531
2532         useDefaultHeader:true,
2533
2534         defaultPostHeader:'application/x-www-form-urlencoded',
2535
2536         useDefaultXhrHeader:true,
2537
2538         defaultXhrHeader:'XMLHttpRequest',
2539
2540         hasDefaultHeaders:true,
2541
2542         defaultHeaders:{},
2543
2544         poll:{},
2545
2546         timeout:{},
2547
2548         pollInterval:50,
2549
2550         transactionId:0,
2551
2552         setProgId:function(id)
2553         {
2554             this.activeX.unshift(id);
2555         },
2556
2557         setDefaultPostHeader:function(b)
2558         {
2559             this.useDefaultHeader = b;
2560         },
2561
2562         setDefaultXhrHeader:function(b)
2563         {
2564             this.useDefaultXhrHeader = b;
2565         },
2566
2567         setPollingInterval:function(i)
2568         {
2569             if (typeof i == 'number' && isFinite(i)) {
2570                 this.pollInterval = i;
2571             }
2572         },
2573
2574         createXhrObject:function(transactionId)
2575         {
2576             var obj,http;
2577             try
2578             {
2579
2580                 http = new XMLHttpRequest();
2581
2582                 obj = { conn:http, tId:transactionId };
2583             }
2584             catch(e)
2585             {
2586                 for (var i = 0; i < this.activeX.length; ++i) {
2587                     try
2588                     {
2589
2590                         http = new ActiveXObject(this.activeX[i]);
2591
2592                         obj = { conn:http, tId:transactionId };
2593                         break;
2594                     }
2595                     catch(e) {
2596                     }
2597                 }
2598             }
2599             finally
2600             {
2601                 return obj;
2602             }
2603         },
2604
2605         getConnectionObject:function()
2606         {
2607             var o;
2608             var tId = this.transactionId;
2609
2610             try
2611             {
2612                 o = this.createXhrObject(tId);
2613                 if (o) {
2614                     this.transactionId++;
2615                 }
2616             }
2617             catch(e) {
2618             }
2619             finally
2620             {
2621                 return o;
2622             }
2623         },
2624
2625         asyncRequest:function(method, uri, callback, postData)
2626         {
2627             var o = this.getConnectionObject();
2628
2629             if (!o) {
2630                 return null;
2631             }
2632             else {
2633                 o.conn.open(method, uri, true);
2634
2635                 if (this.useDefaultXhrHeader) {
2636                     if (!this.defaultHeaders['X-Requested-With']) {
2637                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2638                     }
2639                 }
2640
2641                 if(postData && this.useDefaultHeader){
2642                     this.initHeader('Content-Type', this.defaultPostHeader);
2643                 }
2644
2645                  if (this.hasDefaultHeaders || this.hasHeaders) {
2646                     this.setHeader(o);
2647                 }
2648
2649                 this.handleReadyState(o, callback);
2650                 o.conn.send(postData || null);
2651
2652                 return o;
2653             }
2654         },
2655
2656         handleReadyState:function(o, callback)
2657         {
2658             var oConn = this;
2659
2660             if (callback && callback.timeout) {
2661                 this.timeout[o.tId] = window.setTimeout(function() {
2662                     oConn.abort(o, callback, true);
2663                 }, callback.timeout);
2664             }
2665
2666             this.poll[o.tId] = window.setInterval(
2667                     function() {
2668                         if (o.conn && o.conn.readyState == 4) {
2669                             window.clearInterval(oConn.poll[o.tId]);
2670                             delete oConn.poll[o.tId];
2671
2672                             if(callback && callback.timeout) {
2673                                 window.clearTimeout(oConn.timeout[o.tId]);
2674                                 delete oConn.timeout[o.tId];
2675                             }
2676
2677                             oConn.handleTransactionResponse(o, callback);
2678                         }
2679                     }
2680                     , this.pollInterval);
2681         },
2682
2683         handleTransactionResponse:function(o, callback, isAbort)
2684         {
2685
2686             if (!callback) {
2687                 this.releaseObject(o);
2688                 return;
2689             }
2690
2691             var httpStatus, responseObject;
2692
2693             try
2694             {
2695                 if (o.conn.status !== undefined && o.conn.status != 0) {
2696                     httpStatus = o.conn.status;
2697                 }
2698                 else {
2699                     httpStatus = 13030;
2700                 }
2701             }
2702             catch(e) {
2703
2704
2705                 httpStatus = 13030;
2706             }
2707
2708             if (httpStatus >= 200 && httpStatus < 300) {
2709                 responseObject = this.createResponseObject(o, callback.argument);
2710                 if (callback.success) {
2711                     if (!callback.scope) {
2712                         callback.success(responseObject);
2713                     }
2714                     else {
2715
2716
2717                         callback.success.apply(callback.scope, [responseObject]);
2718                     }
2719                 }
2720             }
2721             else {
2722                 switch (httpStatus) {
2723
2724                     case 12002:
2725                     case 12029:
2726                     case 12030:
2727                     case 12031:
2728                     case 12152:
2729                     case 13030:
2730                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2731                         if (callback.failure) {
2732                             if (!callback.scope) {
2733                                 callback.failure(responseObject);
2734                             }
2735                             else {
2736                                 callback.failure.apply(callback.scope, [responseObject]);
2737                             }
2738                         }
2739                         break;
2740                     default:
2741                         responseObject = this.createResponseObject(o, callback.argument);
2742                         if (callback.failure) {
2743                             if (!callback.scope) {
2744                                 callback.failure(responseObject);
2745                             }
2746                             else {
2747                                 callback.failure.apply(callback.scope, [responseObject]);
2748                             }
2749                         }
2750                 }
2751             }
2752
2753             this.releaseObject(o);
2754             responseObject = null;
2755         },
2756
2757         createResponseObject:function(o, callbackArg)
2758         {
2759             var obj = {};
2760             var headerObj = {};
2761
2762             try
2763             {
2764                 var headerStr = o.conn.getAllResponseHeaders();
2765                 var header = headerStr.split('\n');
2766                 for (var i = 0; i < header.length; i++) {
2767                     var delimitPos = header[i].indexOf(':');
2768                     if (delimitPos != -1) {
2769                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2770                     }
2771                 }
2772             }
2773             catch(e) {
2774             }
2775
2776             obj.tId = o.tId;
2777             obj.status = o.conn.status;
2778             obj.statusText = o.conn.statusText;
2779             obj.getResponseHeader = headerObj;
2780             obj.getAllResponseHeaders = headerStr;
2781             obj.responseText = o.conn.responseText;
2782             obj.responseXML = o.conn.responseXML;
2783
2784             if (typeof callbackArg !== undefined) {
2785                 obj.argument = callbackArg;
2786             }
2787
2788             return obj;
2789         },
2790
2791         createExceptionObject:function(tId, callbackArg, isAbort)
2792         {
2793             var COMM_CODE = 0;
2794             var COMM_ERROR = 'communication failure';
2795             var ABORT_CODE = -1;
2796             var ABORT_ERROR = 'transaction aborted';
2797
2798             var obj = {};
2799
2800             obj.tId = tId;
2801             if (isAbort) {
2802                 obj.status = ABORT_CODE;
2803                 obj.statusText = ABORT_ERROR;
2804             }
2805             else {
2806                 obj.status = COMM_CODE;
2807                 obj.statusText = COMM_ERROR;
2808             }
2809
2810             if (callbackArg) {
2811                 obj.argument = callbackArg;
2812             }
2813
2814             return obj;
2815         },
2816
2817         initHeader:function(label, value, isDefault)
2818         {
2819             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2820
2821             if (headerObj[label] === undefined) {
2822                 headerObj[label] = value;
2823             }
2824             else {
2825
2826
2827                 headerObj[label] = value + "," + headerObj[label];
2828             }
2829
2830             if (isDefault) {
2831                 this.hasDefaultHeaders = true;
2832             }
2833             else {
2834                 this.hasHeaders = true;
2835             }
2836         },
2837
2838
2839         setHeader:function(o)
2840         {
2841             if (this.hasDefaultHeaders) {
2842                 for (var prop in this.defaultHeaders) {
2843                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2844                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2845                     }
2846                 }
2847             }
2848
2849             if (this.hasHeaders) {
2850                 for (var prop in this.headers) {
2851                     if (this.headers.hasOwnProperty(prop)) {
2852                         o.conn.setRequestHeader(prop, this.headers[prop]);
2853                     }
2854                 }
2855                 this.headers = {};
2856                 this.hasHeaders = false;
2857             }
2858         },
2859
2860         resetDefaultHeaders:function() {
2861             delete this.defaultHeaders;
2862             this.defaultHeaders = {};
2863             this.hasDefaultHeaders = false;
2864         },
2865
2866         abort:function(o, callback, isTimeout)
2867         {
2868             if(this.isCallInProgress(o)) {
2869                 o.conn.abort();
2870                 window.clearInterval(this.poll[o.tId]);
2871                 delete this.poll[o.tId];
2872                 if (isTimeout) {
2873                     delete this.timeout[o.tId];
2874                 }
2875
2876                 this.handleTransactionResponse(o, callback, true);
2877
2878                 return true;
2879             }
2880             else {
2881                 return false;
2882             }
2883         },
2884
2885
2886         isCallInProgress:function(o)
2887         {
2888             if (o && o.conn) {
2889                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2890             }
2891             else {
2892
2893                 return false;
2894             }
2895         },
2896
2897
2898         releaseObject:function(o)
2899         {
2900
2901             o.conn = null;
2902
2903             o = null;
2904         },
2905
2906         activeX:[
2907         'MSXML2.XMLHTTP.3.0',
2908         'MSXML2.XMLHTTP',
2909         'Microsoft.XMLHTTP'
2910         ]
2911
2912
2913     };
2914 })();/*
2915  * Portions of this file are based on pieces of Yahoo User Interface Library
2916  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2917  * YUI licensed under the BSD License:
2918  * http://developer.yahoo.net/yui/license.txt
2919  * <script type="text/javascript">
2920  *
2921  */
2922
2923 Roo.lib.Region = function(t, r, b, l) {
2924     this.top = t;
2925     this[1] = t;
2926     this.right = r;
2927     this.bottom = b;
2928     this.left = l;
2929     this[0] = l;
2930 };
2931
2932
2933 Roo.lib.Region.prototype = {
2934     contains : function(region) {
2935         return ( region.left >= this.left &&
2936                  region.right <= this.right &&
2937                  region.top >= this.top &&
2938                  region.bottom <= this.bottom    );
2939
2940     },
2941
2942     getArea : function() {
2943         return ( (this.bottom - this.top) * (this.right - this.left) );
2944     },
2945
2946     intersect : function(region) {
2947         var t = Math.max(this.top, region.top);
2948         var r = Math.min(this.right, region.right);
2949         var b = Math.min(this.bottom, region.bottom);
2950         var l = Math.max(this.left, region.left);
2951
2952         if (b >= t && r >= l) {
2953             return new Roo.lib.Region(t, r, b, l);
2954         } else {
2955             return null;
2956         }
2957     },
2958     union : function(region) {
2959         var t = Math.min(this.top, region.top);
2960         var r = Math.max(this.right, region.right);
2961         var b = Math.max(this.bottom, region.bottom);
2962         var l = Math.min(this.left, region.left);
2963
2964         return new Roo.lib.Region(t, r, b, l);
2965     },
2966
2967     adjust : function(t, l, b, r) {
2968         this.top += t;
2969         this.left += l;
2970         this.right += r;
2971         this.bottom += b;
2972         return this;
2973     }
2974 };
2975
2976 Roo.lib.Region.getRegion = function(el) {
2977     var p = Roo.lib.Dom.getXY(el);
2978
2979     var t = p[1];
2980     var r = p[0] + el.offsetWidth;
2981     var b = p[1] + el.offsetHeight;
2982     var l = p[0];
2983
2984     return new Roo.lib.Region(t, r, b, l);
2985 };
2986 /*
2987  * Portions of this file are based on pieces of Yahoo User Interface Library
2988  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2989  * YUI licensed under the BSD License:
2990  * http://developer.yahoo.net/yui/license.txt
2991  * <script type="text/javascript">
2992  *
2993  */
2994 //@@dep Roo.lib.Region
2995
2996
2997 Roo.lib.Point = function(x, y) {
2998     if (x instanceof Array) {
2999         y = x[1];
3000         x = x[0];
3001     }
3002     this.x = this.right = this.left = this[0] = x;
3003     this.y = this.top = this.bottom = this[1] = y;
3004 };
3005
3006 Roo.lib.Point.prototype = new Roo.lib.Region();
3007 /*
3008  * Portions of this file are based on pieces of Yahoo User Interface Library
3009  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3010  * YUI licensed under the BSD License:
3011  * http://developer.yahoo.net/yui/license.txt
3012  * <script type="text/javascript">
3013  *
3014  */
3015  
3016 (function() {   
3017
3018     Roo.lib.Anim = {
3019         scroll : function(el, args, duration, easing, cb, scope) {
3020             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3021         },
3022
3023         motion : function(el, args, duration, easing, cb, scope) {
3024             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3025         },
3026
3027         color : function(el, args, duration, easing, cb, scope) {
3028             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3029         },
3030
3031         run : function(el, args, duration, easing, cb, scope, type) {
3032             type = type || Roo.lib.AnimBase;
3033             if (typeof easing == "string") {
3034                 easing = Roo.lib.Easing[easing];
3035             }
3036             var anim = new type(el, args, duration, easing);
3037             anim.animateX(function() {
3038                 Roo.callback(cb, scope);
3039             });
3040             return anim;
3041         }
3042     };
3043 })();/*
3044  * Portions of this file are based on pieces of Yahoo User Interface Library
3045  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3046  * YUI licensed under the BSD License:
3047  * http://developer.yahoo.net/yui/license.txt
3048  * <script type="text/javascript">
3049  *
3050  */
3051
3052 (function() {    
3053     var libFlyweight;
3054     
3055     function fly(el) {
3056         if (!libFlyweight) {
3057             libFlyweight = new Roo.Element.Flyweight();
3058         }
3059         libFlyweight.dom = el;
3060         return libFlyweight;
3061     }
3062
3063     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3064     
3065    
3066     
3067     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3068         if (el) {
3069             this.init(el, attributes, duration, method);
3070         }
3071     };
3072
3073     Roo.lib.AnimBase.fly = fly;
3074     
3075     
3076     
3077     Roo.lib.AnimBase.prototype = {
3078
3079         toString: function() {
3080             var el = this.getEl();
3081             var id = el.id || el.tagName;
3082             return ("Anim " + id);
3083         },
3084
3085         patterns: {
3086             noNegatives:        /width|height|opacity|padding/i,
3087             offsetAttribute:  /^((width|height)|(top|left))$/,
3088             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3089             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3090         },
3091
3092
3093         doMethod: function(attr, start, end) {
3094             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3095         },
3096
3097
3098         setAttribute: function(attr, val, unit) {
3099             if (this.patterns.noNegatives.test(attr)) {
3100                 val = (val > 0) ? val : 0;
3101             }
3102
3103             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3104         },
3105
3106
3107         getAttribute: function(attr) {
3108             var el = this.getEl();
3109             var val = fly(el).getStyle(attr);
3110
3111             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3112                 return parseFloat(val);
3113             }
3114
3115             var a = this.patterns.offsetAttribute.exec(attr) || [];
3116             var pos = !!( a[3] );
3117             var box = !!( a[2] );
3118
3119
3120             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3121                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3122             } else {
3123                 val = 0;
3124             }
3125
3126             return val;
3127         },
3128
3129
3130         getDefaultUnit: function(attr) {
3131             if (this.patterns.defaultUnit.test(attr)) {
3132                 return 'px';
3133             }
3134
3135             return '';
3136         },
3137
3138         animateX : function(callback, scope) {
3139             var f = function() {
3140                 this.onComplete.removeListener(f);
3141                 if (typeof callback == "function") {
3142                     callback.call(scope || this, this);
3143                 }
3144             };
3145             this.onComplete.addListener(f, this);
3146             this.animate();
3147         },
3148
3149
3150         setRuntimeAttribute: function(attr) {
3151             var start;
3152             var end;
3153             var attributes = this.attributes;
3154
3155             this.runtimeAttributes[attr] = {};
3156
3157             var isset = function(prop) {
3158                 return (typeof prop !== 'undefined');
3159             };
3160
3161             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3162                 return false;
3163             }
3164
3165             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3166
3167
3168             if (isset(attributes[attr]['to'])) {
3169                 end = attributes[attr]['to'];
3170             } else if (isset(attributes[attr]['by'])) {
3171                 if (start.constructor == Array) {
3172                     end = [];
3173                     for (var i = 0, len = start.length; i < len; ++i) {
3174                         end[i] = start[i] + attributes[attr]['by'][i];
3175                     }
3176                 } else {
3177                     end = start + attributes[attr]['by'];
3178                 }
3179             }
3180
3181             this.runtimeAttributes[attr].start = start;
3182             this.runtimeAttributes[attr].end = end;
3183
3184
3185             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3186         },
3187
3188
3189         init: function(el, attributes, duration, method) {
3190
3191             var isAnimated = false;
3192
3193
3194             var startTime = null;
3195
3196
3197             var actualFrames = 0;
3198
3199
3200             el = Roo.getDom(el);
3201
3202
3203             this.attributes = attributes || {};
3204
3205
3206             this.duration = duration || 1;
3207
3208
3209             this.method = method || Roo.lib.Easing.easeNone;
3210
3211
3212             this.useSeconds = true;
3213
3214
3215             this.currentFrame = 0;
3216
3217
3218             this.totalFrames = Roo.lib.AnimMgr.fps;
3219
3220
3221             this.getEl = function() {
3222                 return el;
3223             };
3224
3225
3226             this.isAnimated = function() {
3227                 return isAnimated;
3228             };
3229
3230
3231             this.getStartTime = function() {
3232                 return startTime;
3233             };
3234
3235             this.runtimeAttributes = {};
3236
3237
3238             this.animate = function() {
3239                 if (this.isAnimated()) {
3240                     return false;
3241                 }
3242
3243                 this.currentFrame = 0;
3244
3245                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3246
3247                 Roo.lib.AnimMgr.registerElement(this);
3248             };
3249
3250
3251             this.stop = function(finish) {
3252                 if (finish) {
3253                     this.currentFrame = this.totalFrames;
3254                     this._onTween.fire();
3255                 }
3256                 Roo.lib.AnimMgr.stop(this);
3257             };
3258
3259             var onStart = function() {
3260                 this.onStart.fire();
3261
3262                 this.runtimeAttributes = {};
3263                 for (var attr in this.attributes) {
3264                     this.setRuntimeAttribute(attr);
3265                 }
3266
3267                 isAnimated = true;
3268                 actualFrames = 0;
3269                 startTime = new Date();
3270             };
3271
3272
3273             var onTween = function() {
3274                 var data = {
3275                     duration: new Date() - this.getStartTime(),
3276                     currentFrame: this.currentFrame
3277                 };
3278
3279                 data.toString = function() {
3280                     return (
3281                             'duration: ' + data.duration +
3282                             ', currentFrame: ' + data.currentFrame
3283                             );
3284                 };
3285
3286                 this.onTween.fire(data);
3287
3288                 var runtimeAttributes = this.runtimeAttributes;
3289
3290                 for (var attr in runtimeAttributes) {
3291                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3292                 }
3293
3294                 actualFrames += 1;
3295             };
3296
3297             var onComplete = function() {
3298                 var actual_duration = (new Date() - startTime) / 1000 ;
3299
3300                 var data = {
3301                     duration: actual_duration,
3302                     frames: actualFrames,
3303                     fps: actualFrames / actual_duration
3304                 };
3305
3306                 data.toString = function() {
3307                     return (
3308                             'duration: ' + data.duration +
3309                             ', frames: ' + data.frames +
3310                             ', fps: ' + data.fps
3311                             );
3312                 };
3313
3314                 isAnimated = false;
3315                 actualFrames = 0;
3316                 this.onComplete.fire(data);
3317             };
3318
3319
3320             this._onStart = new Roo.util.Event(this);
3321             this.onStart = new Roo.util.Event(this);
3322             this.onTween = new Roo.util.Event(this);
3323             this._onTween = new Roo.util.Event(this);
3324             this.onComplete = new Roo.util.Event(this);
3325             this._onComplete = new Roo.util.Event(this);
3326             this._onStart.addListener(onStart);
3327             this._onTween.addListener(onTween);
3328             this._onComplete.addListener(onComplete);
3329         }
3330     };
3331 })();
3332 /*
3333  * Portions of this file are based on pieces of Yahoo User Interface Library
3334  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3335  * YUI licensed under the BSD License:
3336  * http://developer.yahoo.net/yui/license.txt
3337  * <script type="text/javascript">
3338  *
3339  */
3340
3341 Roo.lib.AnimMgr = new function() {
3342
3343         var thread = null;
3344
3345
3346         var queue = [];
3347
3348
3349         var tweenCount = 0;
3350
3351
3352         this.fps = 1000;
3353
3354
3355         this.delay = 1;
3356
3357
3358         this.registerElement = function(tween) {
3359             queue[queue.length] = tween;
3360             tweenCount += 1;
3361             tween._onStart.fire();
3362             this.start();
3363         };
3364
3365
3366         this.unRegister = function(tween, index) {
3367             tween._onComplete.fire();
3368             index = index || getIndex(tween);
3369             if (index != -1) {
3370                 queue.splice(index, 1);
3371             }
3372
3373             tweenCount -= 1;
3374             if (tweenCount <= 0) {
3375                 this.stop();
3376             }
3377         };
3378
3379
3380         this.start = function() {
3381             if (thread === null) {
3382                 thread = setInterval(this.run, this.delay);
3383             }
3384         };
3385
3386
3387         this.stop = function(tween) {
3388             if (!tween) {
3389                 clearInterval(thread);
3390
3391                 for (var i = 0, len = queue.length; i < len; ++i) {
3392                     if (queue[0].isAnimated()) {
3393                         this.unRegister(queue[0], 0);
3394                     }
3395                 }
3396
3397                 queue = [];
3398                 thread = null;
3399                 tweenCount = 0;
3400             }
3401             else {
3402                 this.unRegister(tween);
3403             }
3404         };
3405
3406
3407         this.run = function() {
3408             for (var i = 0, len = queue.length; i < len; ++i) {
3409                 var tween = queue[i];
3410                 if (!tween || !tween.isAnimated()) {
3411                     continue;
3412                 }
3413
3414                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3415                 {
3416                     tween.currentFrame += 1;
3417
3418                     if (tween.useSeconds) {
3419                         correctFrame(tween);
3420                     }
3421                     tween._onTween.fire();
3422                 }
3423                 else {
3424                     Roo.lib.AnimMgr.stop(tween, i);
3425                 }
3426             }
3427         };
3428
3429         var getIndex = function(anim) {
3430             for (var i = 0, len = queue.length; i < len; ++i) {
3431                 if (queue[i] == anim) {
3432                     return i;
3433                 }
3434             }
3435             return -1;
3436         };
3437
3438
3439         var correctFrame = function(tween) {
3440             var frames = tween.totalFrames;
3441             var frame = tween.currentFrame;
3442             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3443             var elapsed = (new Date() - tween.getStartTime());
3444             var tweak = 0;
3445
3446             if (elapsed < tween.duration * 1000) {
3447                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3448             } else {
3449                 tweak = frames - (frame + 1);
3450             }
3451             if (tweak > 0 && isFinite(tweak)) {
3452                 if (tween.currentFrame + tweak >= frames) {
3453                     tweak = frames - (frame + 1);
3454                 }
3455
3456                 tween.currentFrame += tweak;
3457             }
3458         };
3459     };/*
3460  * Portions of this file are based on pieces of Yahoo User Interface Library
3461  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3462  * YUI licensed under the BSD License:
3463  * http://developer.yahoo.net/yui/license.txt
3464  * <script type="text/javascript">
3465  *
3466  */
3467 Roo.lib.Bezier = new function() {
3468
3469         this.getPosition = function(points, t) {
3470             var n = points.length;
3471             var tmp = [];
3472
3473             for (var i = 0; i < n; ++i) {
3474                 tmp[i] = [points[i][0], points[i][1]];
3475             }
3476
3477             for (var j = 1; j < n; ++j) {
3478                 for (i = 0; i < n - j; ++i) {
3479                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3480                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3481                 }
3482             }
3483
3484             return [ tmp[0][0], tmp[0][1] ];
3485
3486         };
3487     };/*
3488  * Portions of this file are based on pieces of Yahoo User Interface Library
3489  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3490  * YUI licensed under the BSD License:
3491  * http://developer.yahoo.net/yui/license.txt
3492  * <script type="text/javascript">
3493  *
3494  */
3495 (function() {
3496
3497     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3498         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3499     };
3500
3501     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3502
3503     var fly = Roo.lib.AnimBase.fly;
3504     var Y = Roo.lib;
3505     var superclass = Y.ColorAnim.superclass;
3506     var proto = Y.ColorAnim.prototype;
3507
3508     proto.toString = function() {
3509         var el = this.getEl();
3510         var id = el.id || el.tagName;
3511         return ("ColorAnim " + id);
3512     };
3513
3514     proto.patterns.color = /color$/i;
3515     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3516     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3517     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3518     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3519
3520
3521     proto.parseColor = function(s) {
3522         if (s.length == 3) {
3523             return s;
3524         }
3525
3526         var c = this.patterns.hex.exec(s);
3527         if (c && c.length == 4) {
3528             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3529         }
3530
3531         c = this.patterns.rgb.exec(s);
3532         if (c && c.length == 4) {
3533             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3534         }
3535
3536         c = this.patterns.hex3.exec(s);
3537         if (c && c.length == 4) {
3538             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3539         }
3540
3541         return null;
3542     };
3543     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3544     proto.getAttribute = function(attr) {
3545         var el = this.getEl();
3546         if (this.patterns.color.test(attr)) {
3547             var val = fly(el).getStyle(attr);
3548
3549             if (this.patterns.transparent.test(val)) {
3550                 var parent = el.parentNode;
3551                 val = fly(parent).getStyle(attr);
3552
3553                 while (parent && this.patterns.transparent.test(val)) {
3554                     parent = parent.parentNode;
3555                     val = fly(parent).getStyle(attr);
3556                     if (parent.tagName.toUpperCase() == 'HTML') {
3557                         val = '#fff';
3558                     }
3559                 }
3560             }
3561         } else {
3562             val = superclass.getAttribute.call(this, attr);
3563         }
3564
3565         return val;
3566     };
3567     proto.getAttribute = function(attr) {
3568         var el = this.getEl();
3569         if (this.patterns.color.test(attr)) {
3570             var val = fly(el).getStyle(attr);
3571
3572             if (this.patterns.transparent.test(val)) {
3573                 var parent = el.parentNode;
3574                 val = fly(parent).getStyle(attr);
3575
3576                 while (parent && this.patterns.transparent.test(val)) {
3577                     parent = parent.parentNode;
3578                     val = fly(parent).getStyle(attr);
3579                     if (parent.tagName.toUpperCase() == 'HTML') {
3580                         val = '#fff';
3581                     }
3582                 }
3583             }
3584         } else {
3585             val = superclass.getAttribute.call(this, attr);
3586         }
3587
3588         return val;
3589     };
3590
3591     proto.doMethod = function(attr, start, end) {
3592         var val;
3593
3594         if (this.patterns.color.test(attr)) {
3595             val = [];
3596             for (var i = 0, len = start.length; i < len; ++i) {
3597                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3598             }
3599
3600             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3601         }
3602         else {
3603             val = superclass.doMethod.call(this, attr, start, end);
3604         }
3605
3606         return val;
3607     };
3608
3609     proto.setRuntimeAttribute = function(attr) {
3610         superclass.setRuntimeAttribute.call(this, attr);
3611
3612         if (this.patterns.color.test(attr)) {
3613             var attributes = this.attributes;
3614             var start = this.parseColor(this.runtimeAttributes[attr].start);
3615             var end = this.parseColor(this.runtimeAttributes[attr].end);
3616
3617             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3618                 end = this.parseColor(attributes[attr].by);
3619
3620                 for (var i = 0, len = start.length; i < len; ++i) {
3621                     end[i] = start[i] + end[i];
3622                 }
3623             }
3624
3625             this.runtimeAttributes[attr].start = start;
3626             this.runtimeAttributes[attr].end = end;
3627         }
3628     };
3629 })();
3630
3631 /*
3632  * Portions of this file are based on pieces of Yahoo User Interface Library
3633  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3634  * YUI licensed under the BSD License:
3635  * http://developer.yahoo.net/yui/license.txt
3636  * <script type="text/javascript">
3637  *
3638  */
3639 Roo.lib.Easing = {
3640
3641
3642     easeNone: function (t, b, c, d) {
3643         return c * t / d + b;
3644     },
3645
3646
3647     easeIn: function (t, b, c, d) {
3648         return c * (t /= d) * t + b;
3649     },
3650
3651
3652     easeOut: function (t, b, c, d) {
3653         return -c * (t /= d) * (t - 2) + b;
3654     },
3655
3656
3657     easeBoth: function (t, b, c, d) {
3658         if ((t /= d / 2) < 1) {
3659             return c / 2 * t * t + b;
3660         }
3661
3662         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3663     },
3664
3665
3666     easeInStrong: function (t, b, c, d) {
3667         return c * (t /= d) * t * t * t + b;
3668     },
3669
3670
3671     easeOutStrong: function (t, b, c, d) {
3672         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3673     },
3674
3675
3676     easeBothStrong: function (t, b, c, d) {
3677         if ((t /= d / 2) < 1) {
3678             return c / 2 * t * t * t * t + b;
3679         }
3680
3681         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3682     },
3683
3684
3685
3686     elasticIn: function (t, b, c, d, a, p) {
3687         if (t == 0) {
3688             return b;
3689         }
3690         if ((t /= d) == 1) {
3691             return b + c;
3692         }
3693         if (!p) {
3694             p = d * .3;
3695         }
3696
3697         if (!a || a < Math.abs(c)) {
3698             a = c;
3699             var s = p / 4;
3700         }
3701         else {
3702             var s = p / (2 * Math.PI) * Math.asin(c / a);
3703         }
3704
3705         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3706     },
3707
3708
3709     elasticOut: function (t, b, c, d, a, p) {
3710         if (t == 0) {
3711             return b;
3712         }
3713         if ((t /= d) == 1) {
3714             return b + c;
3715         }
3716         if (!p) {
3717             p = d * .3;
3718         }
3719
3720         if (!a || a < Math.abs(c)) {
3721             a = c;
3722             var s = p / 4;
3723         }
3724         else {
3725             var s = p / (2 * Math.PI) * Math.asin(c / a);
3726         }
3727
3728         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3729     },
3730
3731
3732     elasticBoth: function (t, b, c, d, a, p) {
3733         if (t == 0) {
3734             return b;
3735         }
3736
3737         if ((t /= d / 2) == 2) {
3738             return b + c;
3739         }
3740
3741         if (!p) {
3742             p = d * (.3 * 1.5);
3743         }
3744
3745         if (!a || a < Math.abs(c)) {
3746             a = c;
3747             var s = p / 4;
3748         }
3749         else {
3750             var s = p / (2 * Math.PI) * Math.asin(c / a);
3751         }
3752
3753         if (t < 1) {
3754             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3755                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3756         }
3757         return a * Math.pow(2, -10 * (t -= 1)) *
3758                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3759     },
3760
3761
3762
3763     backIn: function (t, b, c, d, s) {
3764         if (typeof s == 'undefined') {
3765             s = 1.70158;
3766         }
3767         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3768     },
3769
3770
3771     backOut: function (t, b, c, d, s) {
3772         if (typeof s == 'undefined') {
3773             s = 1.70158;
3774         }
3775         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3776     },
3777
3778
3779     backBoth: function (t, b, c, d, s) {
3780         if (typeof s == 'undefined') {
3781             s = 1.70158;
3782         }
3783
3784         if ((t /= d / 2 ) < 1) {
3785             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3786         }
3787         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3788     },
3789
3790
3791     bounceIn: function (t, b, c, d) {
3792         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3793     },
3794
3795
3796     bounceOut: function (t, b, c, d) {
3797         if ((t /= d) < (1 / 2.75)) {
3798             return c * (7.5625 * t * t) + b;
3799         } else if (t < (2 / 2.75)) {
3800             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3801         } else if (t < (2.5 / 2.75)) {
3802             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3803         }
3804         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3805     },
3806
3807
3808     bounceBoth: function (t, b, c, d) {
3809         if (t < d / 2) {
3810             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3811         }
3812         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3813     }
3814 };/*
3815  * Portions of this file are based on pieces of Yahoo User Interface Library
3816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3817  * YUI licensed under the BSD License:
3818  * http://developer.yahoo.net/yui/license.txt
3819  * <script type="text/javascript">
3820  *
3821  */
3822     (function() {
3823         Roo.lib.Motion = function(el, attributes, duration, method) {
3824             if (el) {
3825                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3826             }
3827         };
3828
3829         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3830
3831
3832         var Y = Roo.lib;
3833         var superclass = Y.Motion.superclass;
3834         var proto = Y.Motion.prototype;
3835
3836         proto.toString = function() {
3837             var el = this.getEl();
3838             var id = el.id || el.tagName;
3839             return ("Motion " + id);
3840         };
3841
3842         proto.patterns.points = /^points$/i;
3843
3844         proto.setAttribute = function(attr, val, unit) {
3845             if (this.patterns.points.test(attr)) {
3846                 unit = unit || 'px';
3847                 superclass.setAttribute.call(this, 'left', val[0], unit);
3848                 superclass.setAttribute.call(this, 'top', val[1], unit);
3849             } else {
3850                 superclass.setAttribute.call(this, attr, val, unit);
3851             }
3852         };
3853
3854         proto.getAttribute = function(attr) {
3855             if (this.patterns.points.test(attr)) {
3856                 var val = [
3857                         superclass.getAttribute.call(this, 'left'),
3858                         superclass.getAttribute.call(this, 'top')
3859                         ];
3860             } else {
3861                 val = superclass.getAttribute.call(this, attr);
3862             }
3863
3864             return val;
3865         };
3866
3867         proto.doMethod = function(attr, start, end) {
3868             var val = null;
3869
3870             if (this.patterns.points.test(attr)) {
3871                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3872                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3873             } else {
3874                 val = superclass.doMethod.call(this, attr, start, end);
3875             }
3876             return val;
3877         };
3878
3879         proto.setRuntimeAttribute = function(attr) {
3880             if (this.patterns.points.test(attr)) {
3881                 var el = this.getEl();
3882                 var attributes = this.attributes;
3883                 var start;
3884                 var control = attributes['points']['control'] || [];
3885                 var end;
3886                 var i, len;
3887
3888                 if (control.length > 0 && !(control[0] instanceof Array)) {
3889                     control = [control];
3890                 } else {
3891                     var tmp = [];
3892                     for (i = 0,len = control.length; i < len; ++i) {
3893                         tmp[i] = control[i];
3894                     }
3895                     control = tmp;
3896                 }
3897
3898                 Roo.fly(el).position();
3899
3900                 if (isset(attributes['points']['from'])) {
3901                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3902                 }
3903                 else {
3904                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3905                 }
3906
3907                 start = this.getAttribute('points');
3908
3909
3910                 if (isset(attributes['points']['to'])) {
3911                     end = translateValues.call(this, attributes['points']['to'], start);
3912
3913                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3914                     for (i = 0,len = control.length; i < len; ++i) {
3915                         control[i] = translateValues.call(this, control[i], start);
3916                     }
3917
3918
3919                 } else if (isset(attributes['points']['by'])) {
3920                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3921
3922                     for (i = 0,len = control.length; i < len; ++i) {
3923                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3924                     }
3925                 }
3926
3927                 this.runtimeAttributes[attr] = [start];
3928
3929                 if (control.length > 0) {
3930                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3931                 }
3932
3933                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3934             }
3935             else {
3936                 superclass.setRuntimeAttribute.call(this, attr);
3937             }
3938         };
3939
3940         var translateValues = function(val, start) {
3941             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3942             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3943
3944             return val;
3945         };
3946
3947         var isset = function(prop) {
3948             return (typeof prop !== 'undefined');
3949         };
3950     })();
3951 /*
3952  * Portions of this file are based on pieces of Yahoo User Interface Library
3953  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3954  * YUI licensed under the BSD License:
3955  * http://developer.yahoo.net/yui/license.txt
3956  * <script type="text/javascript">
3957  *
3958  */
3959     (function() {
3960         Roo.lib.Scroll = function(el, attributes, duration, method) {
3961             if (el) {
3962                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3963             }
3964         };
3965
3966         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3967
3968
3969         var Y = Roo.lib;
3970         var superclass = Y.Scroll.superclass;
3971         var proto = Y.Scroll.prototype;
3972
3973         proto.toString = function() {
3974             var el = this.getEl();
3975             var id = el.id || el.tagName;
3976             return ("Scroll " + id);
3977         };
3978
3979         proto.doMethod = function(attr, start, end) {
3980             var val = null;
3981
3982             if (attr == 'scroll') {
3983                 val = [
3984                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3985                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3986                         ];
3987
3988             } else {
3989                 val = superclass.doMethod.call(this, attr, start, end);
3990             }
3991             return val;
3992         };
3993
3994         proto.getAttribute = function(attr) {
3995             var val = null;
3996             var el = this.getEl();
3997
3998             if (attr == 'scroll') {
3999                 val = [ el.scrollLeft, el.scrollTop ];
4000             } else {
4001                 val = superclass.getAttribute.call(this, attr);
4002             }
4003
4004             return val;
4005         };
4006
4007         proto.setAttribute = function(attr, val, unit) {
4008             var el = this.getEl();
4009
4010             if (attr == 'scroll') {
4011                 el.scrollLeft = val[0];
4012                 el.scrollTop = val[1];
4013             } else {
4014                 superclass.setAttribute.call(this, attr, val, unit);
4015             }
4016         };
4017     })();
4018 /*
4019  * Based on:
4020  * Ext JS Library 1.1.1
4021  * Copyright(c) 2006-2007, Ext JS, LLC.
4022  *
4023  * Originally Released Under LGPL - original licence link has changed is not relivant.
4024  *
4025  * Fork - LGPL
4026  * <script type="text/javascript">
4027  */
4028
4029
4030 // nasty IE9 hack - what a pile of crap that is..
4031
4032  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4033     Range.prototype.createContextualFragment = function (html) {
4034         var doc = window.document;
4035         var container = doc.createElement("div");
4036         container.innerHTML = html;
4037         var frag = doc.createDocumentFragment(), n;
4038         while ((n = container.firstChild)) {
4039             frag.appendChild(n);
4040         }
4041         return frag;
4042     };
4043 }
4044
4045 /**
4046  * @class Roo.DomHelper
4047  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4048  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4049  * @singleton
4050  */
4051 Roo.DomHelper = function(){
4052     var tempTableEl = null;
4053     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4054     var tableRe = /^table|tbody|tr|td$/i;
4055     var xmlns = {};
4056     // build as innerHTML where available
4057     /** @ignore */
4058     var createHtml = function(o){
4059         if(typeof o == 'string'){
4060             return o;
4061         }
4062         var b = "";
4063         if(!o.tag){
4064             o.tag = "div";
4065         }
4066         b += "<" + o.tag;
4067         for(var attr in o){
4068             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4069             if(attr == "style"){
4070                 var s = o["style"];
4071                 if(typeof s == "function"){
4072                     s = s.call();
4073                 }
4074                 if(typeof s == "string"){
4075                     b += ' style="' + s + '"';
4076                 }else if(typeof s == "object"){
4077                     b += ' style="';
4078                     for(var key in s){
4079                         if(typeof s[key] != "function"){
4080                             b += key + ":" + s[key] + ";";
4081                         }
4082                     }
4083                     b += '"';
4084                 }
4085             }else{
4086                 if(attr == "cls"){
4087                     b += ' class="' + o["cls"] + '"';
4088                 }else if(attr == "htmlFor"){
4089                     b += ' for="' + o["htmlFor"] + '"';
4090                 }else{
4091                     b += " " + attr + '="' + o[attr] + '"';
4092                 }
4093             }
4094         }
4095         if(emptyTags.test(o.tag)){
4096             b += "/>";
4097         }else{
4098             b += ">";
4099             var cn = o.children || o.cn;
4100             if(cn){
4101                 //http://bugs.kde.org/show_bug.cgi?id=71506
4102                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4103                     for(var i = 0, len = cn.length; i < len; i++) {
4104                         b += createHtml(cn[i], b);
4105                     }
4106                 }else{
4107                     b += createHtml(cn, b);
4108                 }
4109             }
4110             if(o.html){
4111                 b += o.html;
4112             }
4113             b += "</" + o.tag + ">";
4114         }
4115         return b;
4116     };
4117
4118     // build as dom
4119     /** @ignore */
4120     var createDom = function(o, parentNode){
4121          
4122         // defininition craeted..
4123         var ns = false;
4124         if (o.ns && o.ns != 'html') {
4125                
4126             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4127                 xmlns[o.ns] = o.xmlns;
4128                 ns = o.xmlns;
4129             }
4130             if (typeof(xmlns[o.ns]) == 'undefined') {
4131                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4132             }
4133             ns = xmlns[o.ns];
4134         }
4135         
4136         
4137         if (typeof(o) == 'string') {
4138             return parentNode.appendChild(document.createTextNode(o));
4139         }
4140         o.tag = o.tag || div;
4141         if (o.ns && Roo.isIE) {
4142             ns = false;
4143             o.tag = o.ns + ':' + o.tag;
4144             
4145         }
4146         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4147         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4148         for(var attr in o){
4149             
4150             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4151                     attr == "style" || typeof o[attr] == "function") continue;
4152                     
4153             if(attr=="cls" && Roo.isIE){
4154                 el.className = o["cls"];
4155             }else{
4156                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4157                 else el[attr] = o[attr];
4158             }
4159         }
4160         Roo.DomHelper.applyStyles(el, o.style);
4161         var cn = o.children || o.cn;
4162         if(cn){
4163             //http://bugs.kde.org/show_bug.cgi?id=71506
4164              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4165                 for(var i = 0, len = cn.length; i < len; i++) {
4166                     createDom(cn[i], el);
4167                 }
4168             }else{
4169                 createDom(cn, el);
4170             }
4171         }
4172         if(o.html){
4173             el.innerHTML = o.html;
4174         }
4175         if(parentNode){
4176            parentNode.appendChild(el);
4177         }
4178         return el;
4179     };
4180
4181     var ieTable = function(depth, s, h, e){
4182         tempTableEl.innerHTML = [s, h, e].join('');
4183         var i = -1, el = tempTableEl;
4184         while(++i < depth){
4185             el = el.firstChild;
4186         }
4187         return el;
4188     };
4189
4190     // kill repeat to save bytes
4191     var ts = '<table>',
4192         te = '</table>',
4193         tbs = ts+'<tbody>',
4194         tbe = '</tbody>'+te,
4195         trs = tbs + '<tr>',
4196         tre = '</tr>'+tbe;
4197
4198     /**
4199      * @ignore
4200      * Nasty code for IE's broken table implementation
4201      */
4202     var insertIntoTable = function(tag, where, el, html){
4203         if(!tempTableEl){
4204             tempTableEl = document.createElement('div');
4205         }
4206         var node;
4207         var before = null;
4208         if(tag == 'td'){
4209             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4210                 return;
4211             }
4212             if(where == 'beforebegin'){
4213                 before = el;
4214                 el = el.parentNode;
4215             } else{
4216                 before = el.nextSibling;
4217                 el = el.parentNode;
4218             }
4219             node = ieTable(4, trs, html, tre);
4220         }
4221         else if(tag == 'tr'){
4222             if(where == 'beforebegin'){
4223                 before = el;
4224                 el = el.parentNode;
4225                 node = ieTable(3, tbs, html, tbe);
4226             } else if(where == 'afterend'){
4227                 before = el.nextSibling;
4228                 el = el.parentNode;
4229                 node = ieTable(3, tbs, html, tbe);
4230             } else{ // INTO a TR
4231                 if(where == 'afterbegin'){
4232                     before = el.firstChild;
4233                 }
4234                 node = ieTable(4, trs, html, tre);
4235             }
4236         } else if(tag == 'tbody'){
4237             if(where == 'beforebegin'){
4238                 before = el;
4239                 el = el.parentNode;
4240                 node = ieTable(2, ts, html, te);
4241             } else if(where == 'afterend'){
4242                 before = el.nextSibling;
4243                 el = el.parentNode;
4244                 node = ieTable(2, ts, html, te);
4245             } else{
4246                 if(where == 'afterbegin'){
4247                     before = el.firstChild;
4248                 }
4249                 node = ieTable(3, tbs, html, tbe);
4250             }
4251         } else{ // TABLE
4252             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4253                 return;
4254             }
4255             if(where == 'afterbegin'){
4256                 before = el.firstChild;
4257             }
4258             node = ieTable(2, ts, html, te);
4259         }
4260         el.insertBefore(node, before);
4261         return node;
4262     };
4263
4264     return {
4265     /** True to force the use of DOM instead of html fragments @type Boolean */
4266     useDom : false,
4267
4268     /**
4269      * Returns the markup for the passed Element(s) config
4270      * @param {Object} o The Dom object spec (and children)
4271      * @return {String}
4272      */
4273     markup : function(o){
4274         return createHtml(o);
4275     },
4276
4277     /**
4278      * Applies a style specification to an element
4279      * @param {String/HTMLElement} el The element to apply styles to
4280      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4281      * a function which returns such a specification.
4282      */
4283     applyStyles : function(el, styles){
4284         if(styles){
4285            el = Roo.fly(el);
4286            if(typeof styles == "string"){
4287                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4288                var matches;
4289                while ((matches = re.exec(styles)) != null){
4290                    el.setStyle(matches[1], matches[2]);
4291                }
4292            }else if (typeof styles == "object"){
4293                for (var style in styles){
4294                   el.setStyle(style, styles[style]);
4295                }
4296            }else if (typeof styles == "function"){
4297                 Roo.DomHelper.applyStyles(el, styles.call());
4298            }
4299         }
4300     },
4301
4302     /**
4303      * Inserts an HTML fragment into the Dom
4304      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4305      * @param {HTMLElement} el The context element
4306      * @param {String} html The HTML fragmenet
4307      * @return {HTMLElement} The new node
4308      */
4309     insertHtml : function(where, el, html){
4310         where = where.toLowerCase();
4311         if(el.insertAdjacentHTML){
4312             if(tableRe.test(el.tagName)){
4313                 var rs;
4314                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4315                     return rs;
4316                 }
4317             }
4318             switch(where){
4319                 case "beforebegin":
4320                     el.insertAdjacentHTML('BeforeBegin', html);
4321                     return el.previousSibling;
4322                 case "afterbegin":
4323                     el.insertAdjacentHTML('AfterBegin', html);
4324                     return el.firstChild;
4325                 case "beforeend":
4326                     el.insertAdjacentHTML('BeforeEnd', html);
4327                     return el.lastChild;
4328                 case "afterend":
4329                     el.insertAdjacentHTML('AfterEnd', html);
4330                     return el.nextSibling;
4331             }
4332             throw 'Illegal insertion point -> "' + where + '"';
4333         }
4334         var range = el.ownerDocument.createRange();
4335         var frag;
4336         switch(where){
4337              case "beforebegin":
4338                 range.setStartBefore(el);
4339                 frag = range.createContextualFragment(html);
4340                 el.parentNode.insertBefore(frag, el);
4341                 return el.previousSibling;
4342              case "afterbegin":
4343                 if(el.firstChild){
4344                     range.setStartBefore(el.firstChild);
4345                     frag = range.createContextualFragment(html);
4346                     el.insertBefore(frag, el.firstChild);
4347                     return el.firstChild;
4348                 }else{
4349                     el.innerHTML = html;
4350                     return el.firstChild;
4351                 }
4352             case "beforeend":
4353                 if(el.lastChild){
4354                     range.setStartAfter(el.lastChild);
4355                     frag = range.createContextualFragment(html);
4356                     el.appendChild(frag);
4357                     return el.lastChild;
4358                 }else{
4359                     el.innerHTML = html;
4360                     return el.lastChild;
4361                 }
4362             case "afterend":
4363                 range.setStartAfter(el);
4364                 frag = range.createContextualFragment(html);
4365                 el.parentNode.insertBefore(frag, el.nextSibling);
4366                 return el.nextSibling;
4367             }
4368             throw 'Illegal insertion point -> "' + where + '"';
4369     },
4370
4371     /**
4372      * Creates new Dom element(s) and inserts them before el
4373      * @param {String/HTMLElement/Element} el The context element
4374      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4375      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4376      * @return {HTMLElement/Roo.Element} The new node
4377      */
4378     insertBefore : function(el, o, returnElement){
4379         return this.doInsert(el, o, returnElement, "beforeBegin");
4380     },
4381
4382     /**
4383      * Creates new Dom element(s) and inserts them after el
4384      * @param {String/HTMLElement/Element} el The context element
4385      * @param {Object} o The Dom object spec (and children)
4386      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4387      * @return {HTMLElement/Roo.Element} The new node
4388      */
4389     insertAfter : function(el, o, returnElement){
4390         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4391     },
4392
4393     /**
4394      * Creates new Dom element(s) and inserts them as the first child of el
4395      * @param {String/HTMLElement/Element} el The context element
4396      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4397      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4398      * @return {HTMLElement/Roo.Element} The new node
4399      */
4400     insertFirst : function(el, o, returnElement){
4401         return this.doInsert(el, o, returnElement, "afterBegin");
4402     },
4403
4404     // private
4405     doInsert : function(el, o, returnElement, pos, sibling){
4406         el = Roo.getDom(el);
4407         var newNode;
4408         if(this.useDom || o.ns){
4409             newNode = createDom(o, null);
4410             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4411         }else{
4412             var html = createHtml(o);
4413             newNode = this.insertHtml(pos, el, html);
4414         }
4415         return returnElement ? Roo.get(newNode, true) : newNode;
4416     },
4417
4418     /**
4419      * Creates new Dom element(s) and appends them to el
4420      * @param {String/HTMLElement/Element} el The context element
4421      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4422      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4423      * @return {HTMLElement/Roo.Element} The new node
4424      */
4425     append : function(el, o, returnElement){
4426         el = Roo.getDom(el);
4427         var newNode;
4428         if(this.useDom || o.ns){
4429             newNode = createDom(o, null);
4430             el.appendChild(newNode);
4431         }else{
4432             var html = createHtml(o);
4433             newNode = this.insertHtml("beforeEnd", el, html);
4434         }
4435         return returnElement ? Roo.get(newNode, true) : newNode;
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and overwrites the contents of el with them
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     overwrite : function(el, o, returnElement){
4446         el = Roo.getDom(el);
4447         if (o.ns) {
4448           
4449             while (el.childNodes.length) {
4450                 el.removeChild(el.firstChild);
4451             }
4452             createDom(o, el);
4453         } else {
4454             el.innerHTML = createHtml(o);   
4455         }
4456         
4457         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4458     },
4459
4460     /**
4461      * Creates a new Roo.DomHelper.Template from the Dom object spec
4462      * @param {Object} o The Dom object spec (and children)
4463      * @return {Roo.DomHelper.Template} The new template
4464      */
4465     createTemplate : function(o){
4466         var html = createHtml(o);
4467         return new Roo.Template(html);
4468     }
4469     };
4470 }();
4471 /*
4472  * Based on:
4473  * Ext JS Library 1.1.1
4474  * Copyright(c) 2006-2007, Ext JS, LLC.
4475  *
4476  * Originally Released Under LGPL - original licence link has changed is not relivant.
4477  *
4478  * Fork - LGPL
4479  * <script type="text/javascript">
4480  */
4481  
4482 /**
4483 * @class Roo.Template
4484 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4485 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4486 * Usage:
4487 <pre><code>
4488 var t = new Roo.Template({
4489     html :  '&lt;div name="{id}"&gt;' + 
4490         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4491         '&lt;/div&gt;',
4492     myformat: function (value, allValues) {
4493         return 'XX' + value;
4494     }
4495 });
4496 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4497 </code></pre>
4498 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4499 * @constructor
4500 * @param {Object} cfg - Configuration object.
4501 */
4502 Roo.Template = function(cfg){
4503     // BC!
4504     if(cfg instanceof Array){
4505         cfg = cfg.join("");
4506     }else if(arguments.length > 1){
4507         cfg = Array.prototype.join.call(arguments, "");
4508     }
4509     
4510     
4511     if (typeof(cfg) == 'object') {
4512         Roo.apply(this,cfg)
4513     } else {
4514         // bc
4515         this.html = cfg;
4516     }
4517     
4518     
4519 };
4520 Roo.Template.prototype = {
4521     
4522     /**
4523      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4524      */
4525     html : '',
4526     /**
4527      * Returns an HTML fragment of this template with the specified values applied.
4528      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4529      * @return {String} The HTML fragment
4530      */
4531     applyTemplate : function(values){
4532         try {
4533             
4534             if(this.compiled){
4535                 return this.compiled(values);
4536             }
4537             var useF = this.disableFormats !== true;
4538             var fm = Roo.util.Format, tpl = this;
4539             var fn = function(m, name, format, args){
4540                 if(format && useF){
4541                     if(format.substr(0, 5) == "this."){
4542                         return tpl.call(format.substr(5), values[name], values);
4543                     }else{
4544                         if(args){
4545                             // quoted values are required for strings in compiled templates, 
4546                             // but for non compiled we need to strip them
4547                             // quoted reversed for jsmin
4548                             var re = /^\s*['"](.*)["']\s*$/;
4549                             args = args.split(',');
4550                             for(var i = 0, len = args.length; i < len; i++){
4551                                 args[i] = args[i].replace(re, "$1");
4552                             }
4553                             args = [values[name]].concat(args);
4554                         }else{
4555                             args = [values[name]];
4556                         }
4557                         return fm[format].apply(fm, args);
4558                     }
4559                 }else{
4560                     return values[name] !== undefined ? values[name] : "";
4561                 }
4562             };
4563             return this.html.replace(this.re, fn);
4564         } catch (e) {
4565             Roo.log(e);
4566             throw e;
4567         }
4568          
4569     },
4570     
4571     /**
4572      * Sets the HTML used as the template and optionally compiles it.
4573      * @param {String} html
4574      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4575      * @return {Roo.Template} this
4576      */
4577     set : function(html, compile){
4578         this.html = html;
4579         this.compiled = null;
4580         if(compile){
4581             this.compile();
4582         }
4583         return this;
4584     },
4585     
4586     /**
4587      * True to disable format functions (defaults to false)
4588      * @type Boolean
4589      */
4590     disableFormats : false,
4591     
4592     /**
4593     * The regular expression used to match template variables 
4594     * @type RegExp
4595     * @property 
4596     */
4597     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4598     
4599     /**
4600      * Compiles the template into an internal function, eliminating the RegEx overhead.
4601      * @return {Roo.Template} this
4602      */
4603     compile : function(){
4604         var fm = Roo.util.Format;
4605         var useF = this.disableFormats !== true;
4606         var sep = Roo.isGecko ? "+" : ",";
4607         var fn = function(m, name, format, args){
4608             if(format && useF){
4609                 args = args ? ',' + args : "";
4610                 if(format.substr(0, 5) != "this."){
4611                     format = "fm." + format + '(';
4612                 }else{
4613                     format = 'this.call("'+ format.substr(5) + '", ';
4614                     args = ", values";
4615                 }
4616             }else{
4617                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4618             }
4619             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4620         };
4621         var body;
4622         // branched to use + in gecko and [].join() in others
4623         if(Roo.isGecko){
4624             body = "this.compiled = function(values){ return '" +
4625                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4626                     "';};";
4627         }else{
4628             body = ["this.compiled = function(values){ return ['"];
4629             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4630             body.push("'].join('');};");
4631             body = body.join('');
4632         }
4633         /**
4634          * eval:var:values
4635          * eval:var:fm
4636          */
4637         eval(body);
4638         return this;
4639     },
4640     
4641     // private function used to call members
4642     call : function(fnName, value, allValues){
4643         return this[fnName](value, allValues);
4644     },
4645     
4646     /**
4647      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4648      * @param {String/HTMLElement/Roo.Element} el The context element
4649      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4650      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4651      * @return {HTMLElement/Roo.Element} The new node or Element
4652      */
4653     insertFirst: function(el, values, returnElement){
4654         return this.doInsert('afterBegin', el, values, returnElement);
4655     },
4656
4657     /**
4658      * Applies the supplied values to the template and inserts the new node(s) before el.
4659      * @param {String/HTMLElement/Roo.Element} el The context element
4660      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4661      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4662      * @return {HTMLElement/Roo.Element} The new node or Element
4663      */
4664     insertBefore: function(el, values, returnElement){
4665         return this.doInsert('beforeBegin', el, values, returnElement);
4666     },
4667
4668     /**
4669      * Applies the supplied values to the template and inserts the new node(s) after el.
4670      * @param {String/HTMLElement/Roo.Element} el The context element
4671      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4672      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4673      * @return {HTMLElement/Roo.Element} The new node or Element
4674      */
4675     insertAfter : function(el, values, returnElement){
4676         return this.doInsert('afterEnd', el, values, returnElement);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and appends the new node(s) to el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     append : function(el, values, returnElement){
4687         return this.doInsert('beforeEnd', el, values, returnElement);
4688     },
4689
4690     doInsert : function(where, el, values, returnEl){
4691         el = Roo.getDom(el);
4692         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4693         return returnEl ? Roo.get(newNode, true) : newNode;
4694     },
4695
4696     /**
4697      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4698      * @param {String/HTMLElement/Roo.Element} el The context element
4699      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4700      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4701      * @return {HTMLElement/Roo.Element} The new node or Element
4702      */
4703     overwrite : function(el, values, returnElement){
4704         el = Roo.getDom(el);
4705         el.innerHTML = this.applyTemplate(values);
4706         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4707     }
4708 };
4709 /**
4710  * Alias for {@link #applyTemplate}
4711  * @method
4712  */
4713 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4714
4715 // backwards compat
4716 Roo.DomHelper.Template = Roo.Template;
4717
4718 /**
4719  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4720  * @param {String/HTMLElement} el A DOM element or its id
4721  * @returns {Roo.Template} The created template
4722  * @static
4723  */
4724 Roo.Template.from = function(el){
4725     el = Roo.getDom(el);
4726     return new Roo.Template(el.value || el.innerHTML);
4727 };/*
4728  * Based on:
4729  * Ext JS Library 1.1.1
4730  * Copyright(c) 2006-2007, Ext JS, LLC.
4731  *
4732  * Originally Released Under LGPL - original licence link has changed is not relivant.
4733  *
4734  * Fork - LGPL
4735  * <script type="text/javascript">
4736  */
4737  
4738
4739 /*
4740  * This is code is also distributed under MIT license for use
4741  * with jQuery and prototype JavaScript libraries.
4742  */
4743 /**
4744  * @class Roo.DomQuery
4745 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4746 <p>
4747 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4748
4749 <p>
4750 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4751 </p>
4752 <h4>Element Selectors:</h4>
4753 <ul class="list">
4754     <li> <b>*</b> any element</li>
4755     <li> <b>E</b> an element with the tag E</li>
4756     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4757     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4758     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4759     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4760 </ul>
4761 <h4>Attribute Selectors:</h4>
4762 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4763 <ul class="list">
4764     <li> <b>E[foo]</b> has an attribute "foo"</li>
4765     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4766     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4767     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4768     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4769     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4770     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4771 </ul>
4772 <h4>Pseudo Classes:</h4>
4773 <ul class="list">
4774     <li> <b>E:first-child</b> E is the first child of its parent</li>
4775     <li> <b>E:last-child</b> E is the last child of its parent</li>
4776     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4777     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4778     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4779     <li> <b>E:only-child</b> E is the only child of its parent</li>
4780     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4781     <li> <b>E:first</b> the first E in the resultset</li>
4782     <li> <b>E:last</b> the last E in the resultset</li>
4783     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4784     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4785     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4786     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4787     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4788     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4789     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4790     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4791     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4792 </ul>
4793 <h4>CSS Value Selectors:</h4>
4794 <ul class="list">
4795     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4796     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4797     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4798     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4799     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4800     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4801 </ul>
4802  * @singleton
4803  */
4804 Roo.DomQuery = function(){
4805     var cache = {}, simpleCache = {}, valueCache = {};
4806     var nonSpace = /\S/;
4807     var trimRe = /^\s+|\s+$/g;
4808     var tplRe = /\{(\d+)\}/g;
4809     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4810     var tagTokenRe = /^(#)?([\w-\*]+)/;
4811     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4812
4813     function child(p, index){
4814         var i = 0;
4815         var n = p.firstChild;
4816         while(n){
4817             if(n.nodeType == 1){
4818                if(++i == index){
4819                    return n;
4820                }
4821             }
4822             n = n.nextSibling;
4823         }
4824         return null;
4825     };
4826
4827     function next(n){
4828         while((n = n.nextSibling) && n.nodeType != 1);
4829         return n;
4830     };
4831
4832     function prev(n){
4833         while((n = n.previousSibling) && n.nodeType != 1);
4834         return n;
4835     };
4836
4837     function children(d){
4838         var n = d.firstChild, ni = -1;
4839             while(n){
4840                 var nx = n.nextSibling;
4841                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4842                     d.removeChild(n);
4843                 }else{
4844                     n.nodeIndex = ++ni;
4845                 }
4846                 n = nx;
4847             }
4848             return this;
4849         };
4850
4851     function byClassName(c, a, v){
4852         if(!v){
4853             return c;
4854         }
4855         var r = [], ri = -1, cn;
4856         for(var i = 0, ci; ci = c[i]; i++){
4857             if((' '+ci.className+' ').indexOf(v) != -1){
4858                 r[++ri] = ci;
4859             }
4860         }
4861         return r;
4862     };
4863
4864     function attrValue(n, attr){
4865         if(!n.tagName && typeof n.length != "undefined"){
4866             n = n[0];
4867         }
4868         if(!n){
4869             return null;
4870         }
4871         if(attr == "for"){
4872             return n.htmlFor;
4873         }
4874         if(attr == "class" || attr == "className"){
4875             return n.className;
4876         }
4877         return n.getAttribute(attr) || n[attr];
4878
4879     };
4880
4881     function getNodes(ns, mode, tagName){
4882         var result = [], ri = -1, cs;
4883         if(!ns){
4884             return result;
4885         }
4886         tagName = tagName || "*";
4887         if(typeof ns.getElementsByTagName != "undefined"){
4888             ns = [ns];
4889         }
4890         if(!mode){
4891             for(var i = 0, ni; ni = ns[i]; i++){
4892                 cs = ni.getElementsByTagName(tagName);
4893                 for(var j = 0, ci; ci = cs[j]; j++){
4894                     result[++ri] = ci;
4895                 }
4896             }
4897         }else if(mode == "/" || mode == ">"){
4898             var utag = tagName.toUpperCase();
4899             for(var i = 0, ni, cn; ni = ns[i]; i++){
4900                 cn = ni.children || ni.childNodes;
4901                 for(var j = 0, cj; cj = cn[j]; j++){
4902                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4903                         result[++ri] = cj;
4904                     }
4905                 }
4906             }
4907         }else if(mode == "+"){
4908             var utag = tagName.toUpperCase();
4909             for(var i = 0, n; n = ns[i]; i++){
4910                 while((n = n.nextSibling) && n.nodeType != 1);
4911                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4912                     result[++ri] = n;
4913                 }
4914             }
4915         }else if(mode == "~"){
4916             for(var i = 0, n; n = ns[i]; i++){
4917                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4918                 if(n){
4919                     result[++ri] = n;
4920                 }
4921             }
4922         }
4923         return result;
4924     };
4925
4926     function concat(a, b){
4927         if(b.slice){
4928             return a.concat(b);
4929         }
4930         for(var i = 0, l = b.length; i < l; i++){
4931             a[a.length] = b[i];
4932         }
4933         return a;
4934     }
4935
4936     function byTag(cs, tagName){
4937         if(cs.tagName || cs == document){
4938             cs = [cs];
4939         }
4940         if(!tagName){
4941             return cs;
4942         }
4943         var r = [], ri = -1;
4944         tagName = tagName.toLowerCase();
4945         for(var i = 0, ci; ci = cs[i]; i++){
4946             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4947                 r[++ri] = ci;
4948             }
4949         }
4950         return r;
4951     };
4952
4953     function byId(cs, attr, id){
4954         if(cs.tagName || cs == document){
4955             cs = [cs];
4956         }
4957         if(!id){
4958             return cs;
4959         }
4960         var r = [], ri = -1;
4961         for(var i = 0,ci; ci = cs[i]; i++){
4962             if(ci && ci.id == id){
4963                 r[++ri] = ci;
4964                 return r;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function byAttribute(cs, attr, value, op, custom){
4971         var r = [], ri = -1, st = custom=="{";
4972         var f = Roo.DomQuery.operators[op];
4973         for(var i = 0, ci; ci = cs[i]; i++){
4974             var a;
4975             if(st){
4976                 a = Roo.DomQuery.getStyle(ci, attr);
4977             }
4978             else if(attr == "class" || attr == "className"){
4979                 a = ci.className;
4980             }else if(attr == "for"){
4981                 a = ci.htmlFor;
4982             }else if(attr == "href"){
4983                 a = ci.getAttribute("href", 2);
4984             }else{
4985                 a = ci.getAttribute(attr);
4986             }
4987             if((f && f(a, value)) || (!f && a)){
4988                 r[++ri] = ci;
4989             }
4990         }
4991         return r;
4992     };
4993
4994     function byPseudo(cs, name, value){
4995         return Roo.DomQuery.pseudos[name](cs, value);
4996     };
4997
4998     // This is for IE MSXML which does not support expandos.
4999     // IE runs the same speed using setAttribute, however FF slows way down
5000     // and Safari completely fails so they need to continue to use expandos.
5001     var isIE = window.ActiveXObject ? true : false;
5002
5003     // this eval is stop the compressor from
5004     // renaming the variable to something shorter
5005     
5006     /** eval:var:batch */
5007     var batch = 30803; 
5008
5009     var key = 30803;
5010
5011     function nodupIEXml(cs){
5012         var d = ++key;
5013         cs[0].setAttribute("_nodup", d);
5014         var r = [cs[0]];
5015         for(var i = 1, len = cs.length; i < len; i++){
5016             var c = cs[i];
5017             if(!c.getAttribute("_nodup") != d){
5018                 c.setAttribute("_nodup", d);
5019                 r[r.length] = c;
5020             }
5021         }
5022         for(var i = 0, len = cs.length; i < len; i++){
5023             cs[i].removeAttribute("_nodup");
5024         }
5025         return r;
5026     }
5027
5028     function nodup(cs){
5029         if(!cs){
5030             return [];
5031         }
5032         var len = cs.length, c, i, r = cs, cj, ri = -1;
5033         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5034             return cs;
5035         }
5036         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5037             return nodupIEXml(cs);
5038         }
5039         var d = ++key;
5040         cs[0]._nodup = d;
5041         for(i = 1; c = cs[i]; i++){
5042             if(c._nodup != d){
5043                 c._nodup = d;
5044             }else{
5045                 r = [];
5046                 for(var j = 0; j < i; j++){
5047                     r[++ri] = cs[j];
5048                 }
5049                 for(j = i+1; cj = cs[j]; j++){
5050                     if(cj._nodup != d){
5051                         cj._nodup = d;
5052                         r[++ri] = cj;
5053                     }
5054                 }
5055                 return r;
5056             }
5057         }
5058         return r;
5059     }
5060
5061     function quickDiffIEXml(c1, c2){
5062         var d = ++key;
5063         for(var i = 0, len = c1.length; i < len; i++){
5064             c1[i].setAttribute("_qdiff", d);
5065         }
5066         var r = [];
5067         for(var i = 0, len = c2.length; i < len; i++){
5068             if(c2[i].getAttribute("_qdiff") != d){
5069                 r[r.length] = c2[i];
5070             }
5071         }
5072         for(var i = 0, len = c1.length; i < len; i++){
5073            c1[i].removeAttribute("_qdiff");
5074         }
5075         return r;
5076     }
5077
5078     function quickDiff(c1, c2){
5079         var len1 = c1.length;
5080         if(!len1){
5081             return c2;
5082         }
5083         if(isIE && c1[0].selectSingleNode){
5084             return quickDiffIEXml(c1, c2);
5085         }
5086         var d = ++key;
5087         for(var i = 0; i < len1; i++){
5088             c1[i]._qdiff = d;
5089         }
5090         var r = [];
5091         for(var i = 0, len = c2.length; i < len; i++){
5092             if(c2[i]._qdiff != d){
5093                 r[r.length] = c2[i];
5094             }
5095         }
5096         return r;
5097     }
5098
5099     function quickId(ns, mode, root, id){
5100         if(ns == root){
5101            var d = root.ownerDocument || root;
5102            return d.getElementById(id);
5103         }
5104         ns = getNodes(ns, mode, "*");
5105         return byId(ns, null, id);
5106     }
5107
5108     return {
5109         getStyle : function(el, name){
5110             return Roo.fly(el).getStyle(name);
5111         },
5112         /**
5113          * Compiles a selector/xpath query into a reusable function. The returned function
5114          * takes one parameter "root" (optional), which is the context node from where the query should start.
5115          * @param {String} selector The selector/xpath query
5116          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5117          * @return {Function}
5118          */
5119         compile : function(path, type){
5120             type = type || "select";
5121             
5122             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5123             var q = path, mode, lq;
5124             var tk = Roo.DomQuery.matchers;
5125             var tklen = tk.length;
5126             var mm;
5127
5128             // accept leading mode switch
5129             var lmode = q.match(modeRe);
5130             if(lmode && lmode[1]){
5131                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5132                 q = q.replace(lmode[1], "");
5133             }
5134             // strip leading slashes
5135             while(path.substr(0, 1)=="/"){
5136                 path = path.substr(1);
5137             }
5138
5139             while(q && lq != q){
5140                 lq = q;
5141                 var tm = q.match(tagTokenRe);
5142                 if(type == "select"){
5143                     if(tm){
5144                         if(tm[1] == "#"){
5145                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5146                         }else{
5147                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5148                         }
5149                         q = q.replace(tm[0], "");
5150                     }else if(q.substr(0, 1) != '@'){
5151                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5152                     }
5153                 }else{
5154                     if(tm){
5155                         if(tm[1] == "#"){
5156                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5157                         }else{
5158                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5159                         }
5160                         q = q.replace(tm[0], "");
5161                     }
5162                 }
5163                 while(!(mm = q.match(modeRe))){
5164                     var matched = false;
5165                     for(var j = 0; j < tklen; j++){
5166                         var t = tk[j];
5167                         var m = q.match(t.re);
5168                         if(m){
5169                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5170                                                     return m[i];
5171                                                 });
5172                             q = q.replace(m[0], "");
5173                             matched = true;
5174                             break;
5175                         }
5176                     }
5177                     // prevent infinite loop on bad selector
5178                     if(!matched){
5179                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5180                     }
5181                 }
5182                 if(mm[1]){
5183                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5184                     q = q.replace(mm[1], "");
5185                 }
5186             }
5187             fn[fn.length] = "return nodup(n);\n}";
5188             
5189              /** 
5190               * list of variables that need from compression as they are used by eval.
5191              *  eval:var:batch 
5192              *  eval:var:nodup
5193              *  eval:var:byTag
5194              *  eval:var:ById
5195              *  eval:var:getNodes
5196              *  eval:var:quickId
5197              *  eval:var:mode
5198              *  eval:var:root
5199              *  eval:var:n
5200              *  eval:var:byClassName
5201              *  eval:var:byPseudo
5202              *  eval:var:byAttribute
5203              *  eval:var:attrValue
5204              * 
5205              **/ 
5206             eval(fn.join(""));
5207             return f;
5208         },
5209
5210         /**
5211          * Selects a group of elements.
5212          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5213          * @param {Node} root (optional) The start of the query (defaults to document).
5214          * @return {Array}
5215          */
5216         select : function(path, root, type){
5217             if(!root || root == document){
5218                 root = document;
5219             }
5220             if(typeof root == "string"){
5221                 root = document.getElementById(root);
5222             }
5223             var paths = path.split(",");
5224             var results = [];
5225             for(var i = 0, len = paths.length; i < len; i++){
5226                 var p = paths[i].replace(trimRe, "");
5227                 if(!cache[p]){
5228                     cache[p] = Roo.DomQuery.compile(p);
5229                     if(!cache[p]){
5230                         throw p + " is not a valid selector";
5231                     }
5232                 }
5233                 var result = cache[p](root);
5234                 if(result && result != document){
5235                     results = results.concat(result);
5236                 }
5237             }
5238             if(paths.length > 1){
5239                 return nodup(results);
5240             }
5241             return results;
5242         },
5243
5244         /**
5245          * Selects a single element.
5246          * @param {String} selector The selector/xpath query
5247          * @param {Node} root (optional) The start of the query (defaults to document).
5248          * @return {Element}
5249          */
5250         selectNode : function(path, root){
5251             return Roo.DomQuery.select(path, root)[0];
5252         },
5253
5254         /**
5255          * Selects the value of a node, optionally replacing null with the defaultValue.
5256          * @param {String} selector The selector/xpath query
5257          * @param {Node} root (optional) The start of the query (defaults to document).
5258          * @param {String} defaultValue
5259          */
5260         selectValue : function(path, root, defaultValue){
5261             path = path.replace(trimRe, "");
5262             if(!valueCache[path]){
5263                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5264             }
5265             var n = valueCache[path](root);
5266             n = n[0] ? n[0] : n;
5267             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5268             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5269         },
5270
5271         /**
5272          * Selects the value of a node, parsing integers and floats.
5273          * @param {String} selector The selector/xpath query
5274          * @param {Node} root (optional) The start of the query (defaults to document).
5275          * @param {Number} defaultValue
5276          * @return {Number}
5277          */
5278         selectNumber : function(path, root, defaultValue){
5279             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5280             return parseFloat(v);
5281         },
5282
5283         /**
5284          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5285          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5286          * @param {String} selector The simple selector to test
5287          * @return {Boolean}
5288          */
5289         is : function(el, ss){
5290             if(typeof el == "string"){
5291                 el = document.getElementById(el);
5292             }
5293             var isArray = (el instanceof Array);
5294             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5295             return isArray ? (result.length == el.length) : (result.length > 0);
5296         },
5297
5298         /**
5299          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5300          * @param {Array} el An array of elements to filter
5301          * @param {String} selector The simple selector to test
5302          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5303          * the selector instead of the ones that match
5304          * @return {Array}
5305          */
5306         filter : function(els, ss, nonMatches){
5307             ss = ss.replace(trimRe, "");
5308             if(!simpleCache[ss]){
5309                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5310             }
5311             var result = simpleCache[ss](els);
5312             return nonMatches ? quickDiff(result, els) : result;
5313         },
5314
5315         /**
5316          * Collection of matching regular expressions and code snippets.
5317          */
5318         matchers : [{
5319                 re: /^\.([\w-]+)/,
5320                 select: 'n = byClassName(n, null, " {1} ");'
5321             }, {
5322                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5323                 select: 'n = byPseudo(n, "{1}", "{2}");'
5324             },{
5325                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5326                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5327             }, {
5328                 re: /^#([\w-]+)/,
5329                 select: 'n = byId(n, null, "{1}");'
5330             },{
5331                 re: /^@([\w-]+)/,
5332                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5333             }
5334         ],
5335
5336         /**
5337          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5338          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5339          */
5340         operators : {
5341             "=" : function(a, v){
5342                 return a == v;
5343             },
5344             "!=" : function(a, v){
5345                 return a != v;
5346             },
5347             "^=" : function(a, v){
5348                 return a && a.substr(0, v.length) == v;
5349             },
5350             "$=" : function(a, v){
5351                 return a && a.substr(a.length-v.length) == v;
5352             },
5353             "*=" : function(a, v){
5354                 return a && a.indexOf(v) !== -1;
5355             },
5356             "%=" : function(a, v){
5357                 return (a % v) == 0;
5358             },
5359             "|=" : function(a, v){
5360                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5361             },
5362             "~=" : function(a, v){
5363                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5364             }
5365         },
5366
5367         /**
5368          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5369          * and the argument (if any) supplied in the selector.
5370          */
5371         pseudos : {
5372             "first-child" : function(c){
5373                 var r = [], ri = -1, n;
5374                 for(var i = 0, ci; ci = n = c[i]; i++){
5375                     while((n = n.previousSibling) && n.nodeType != 1);
5376                     if(!n){
5377                         r[++ri] = ci;
5378                     }
5379                 }
5380                 return r;
5381             },
5382
5383             "last-child" : function(c){
5384                 var r = [], ri = -1, n;
5385                 for(var i = 0, ci; ci = n = c[i]; i++){
5386                     while((n = n.nextSibling) && n.nodeType != 1);
5387                     if(!n){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "nth-child" : function(c, a) {
5395                 var r = [], ri = -1;
5396                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5397                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5398                 for(var i = 0, n; n = c[i]; i++){
5399                     var pn = n.parentNode;
5400                     if (batch != pn._batch) {
5401                         var j = 0;
5402                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5403                             if(cn.nodeType == 1){
5404                                cn.nodeIndex = ++j;
5405                             }
5406                         }
5407                         pn._batch = batch;
5408                     }
5409                     if (f == 1) {
5410                         if (l == 0 || n.nodeIndex == l){
5411                             r[++ri] = n;
5412                         }
5413                     } else if ((n.nodeIndex + l) % f == 0){
5414                         r[++ri] = n;
5415                     }
5416                 }
5417
5418                 return r;
5419             },
5420
5421             "only-child" : function(c){
5422                 var r = [], ri = -1;;
5423                 for(var i = 0, ci; ci = c[i]; i++){
5424                     if(!prev(ci) && !next(ci)){
5425                         r[++ri] = ci;
5426                     }
5427                 }
5428                 return r;
5429             },
5430
5431             "empty" : function(c){
5432                 var r = [], ri = -1;
5433                 for(var i = 0, ci; ci = c[i]; i++){
5434                     var cns = ci.childNodes, j = 0, cn, empty = true;
5435                     while(cn = cns[j]){
5436                         ++j;
5437                         if(cn.nodeType == 1 || cn.nodeType == 3){
5438                             empty = false;
5439                             break;
5440                         }
5441                     }
5442                     if(empty){
5443                         r[++ri] = ci;
5444                     }
5445                 }
5446                 return r;
5447             },
5448
5449             "contains" : function(c, v){
5450                 var r = [], ri = -1;
5451                 for(var i = 0, ci; ci = c[i]; i++){
5452                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5453                         r[++ri] = ci;
5454                     }
5455                 }
5456                 return r;
5457             },
5458
5459             "nodeValue" : function(c, v){
5460                 var r = [], ri = -1;
5461                 for(var i = 0, ci; ci = c[i]; i++){
5462                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5463                         r[++ri] = ci;
5464                     }
5465                 }
5466                 return r;
5467             },
5468
5469             "checked" : function(c){
5470                 var r = [], ri = -1;
5471                 for(var i = 0, ci; ci = c[i]; i++){
5472                     if(ci.checked == true){
5473                         r[++ri] = ci;
5474                     }
5475                 }
5476                 return r;
5477             },
5478
5479             "not" : function(c, ss){
5480                 return Roo.DomQuery.filter(c, ss, true);
5481             },
5482
5483             "odd" : function(c){
5484                 return this["nth-child"](c, "odd");
5485             },
5486
5487             "even" : function(c){
5488                 return this["nth-child"](c, "even");
5489             },
5490
5491             "nth" : function(c, a){
5492                 return c[a-1] || [];
5493             },
5494
5495             "first" : function(c){
5496                 return c[0] || [];
5497             },
5498
5499             "last" : function(c){
5500                 return c[c.length-1] || [];
5501             },
5502
5503             "has" : function(c, ss){
5504                 var s = Roo.DomQuery.select;
5505                 var r = [], ri = -1;
5506                 for(var i = 0, ci; ci = c[i]; i++){
5507                     if(s(ss, ci).length > 0){
5508                         r[++ri] = ci;
5509                     }
5510                 }
5511                 return r;
5512             },
5513
5514             "next" : function(c, ss){
5515                 var is = Roo.DomQuery.is;
5516                 var r = [], ri = -1;
5517                 for(var i = 0, ci; ci = c[i]; i++){
5518                     var n = next(ci);
5519                     if(n && is(n, ss)){
5520                         r[++ri] = ci;
5521                     }
5522                 }
5523                 return r;
5524             },
5525
5526             "prev" : function(c, ss){
5527                 var is = Roo.DomQuery.is;
5528                 var r = [], ri = -1;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     var n = prev(ci);
5531                     if(n && is(n, ss)){
5532                         r[++ri] = ci;
5533                     }
5534                 }
5535                 return r;
5536             }
5537         }
5538     };
5539 }();
5540
5541 /**
5542  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5543  * @param {String} path The selector/xpath query
5544  * @param {Node} root (optional) The start of the query (defaults to document).
5545  * @return {Array}
5546  * @member Roo
5547  * @method query
5548  */
5549 Roo.query = Roo.DomQuery.select;
5550 /*
5551  * Based on:
5552  * Ext JS Library 1.1.1
5553  * Copyright(c) 2006-2007, Ext JS, LLC.
5554  *
5555  * Originally Released Under LGPL - original licence link has changed is not relivant.
5556  *
5557  * Fork - LGPL
5558  * <script type="text/javascript">
5559  */
5560
5561 /**
5562  * @class Roo.util.Observable
5563  * Base class that provides a common interface for publishing events. Subclasses are expected to
5564  * to have a property "events" with all the events defined.<br>
5565  * For example:
5566  * <pre><code>
5567  Employee = function(name){
5568     this.name = name;
5569     this.addEvents({
5570         "fired" : true,
5571         "quit" : true
5572     });
5573  }
5574  Roo.extend(Employee, Roo.util.Observable);
5575 </code></pre>
5576  * @param {Object} config properties to use (incuding events / listeners)
5577  */
5578
5579 Roo.util.Observable = function(cfg){
5580     
5581     cfg = cfg|| {};
5582     this.addEvents(cfg.events || {});
5583     if (cfg.events) {
5584         delete cfg.events; // make sure
5585     }
5586      
5587     Roo.apply(this, cfg);
5588     
5589     if(this.listeners){
5590         this.on(this.listeners);
5591         delete this.listeners;
5592     }
5593 };
5594 Roo.util.Observable.prototype = {
5595     /** 
5596  * @cfg {Object} listeners  list of events and functions to call for this object, 
5597  * For example :
5598  * <pre><code>
5599     listeners :  { 
5600        'click' : function(e) {
5601            ..... 
5602         } ,
5603         .... 
5604     } 
5605   </code></pre>
5606  */
5607     
5608     
5609     /**
5610      * Fires the specified event with the passed parameters (minus the event name).
5611      * @param {String} eventName
5612      * @param {Object...} args Variable number of parameters are passed to handlers
5613      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5614      */
5615     fireEvent : function(){
5616         var ce = this.events[arguments[0].toLowerCase()];
5617         if(typeof ce == "object"){
5618             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5619         }else{
5620             return true;
5621         }
5622     },
5623
5624     // private
5625     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5626
5627     /**
5628      * Appends an event handler to this component
5629      * @param {String}   eventName The type of event to listen for
5630      * @param {Function} handler The method the event invokes
5631      * @param {Object}   scope (optional) The scope in which to execute the handler
5632      * function. The handler function's "this" context.
5633      * @param {Object}   options (optional) An object containing handler configuration
5634      * properties. This may contain any of the following properties:<ul>
5635      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5636      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5637      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5638      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5639      * by the specified number of milliseconds. If the event fires again within that time, the original
5640      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5641      * </ul><br>
5642      * <p>
5643      * <b>Combining Options</b><br>
5644      * Using the options argument, it is possible to combine different types of listeners:<br>
5645      * <br>
5646      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5647                 <pre><code>
5648                 el.on('click', this.onClick, this, {
5649                         single: true,
5650                 delay: 100,
5651                 forumId: 4
5652                 });
5653                 </code></pre>
5654      * <p>
5655      * <b>Attaching multiple handlers in 1 call</b><br>
5656      * The method also allows for a single argument to be passed which is a config object containing properties
5657      * which specify multiple handlers.
5658      * <pre><code>
5659                 el.on({
5660                         'click': {
5661                         fn: this.onClick,
5662                         scope: this,
5663                         delay: 100
5664                 }, 
5665                 'mouseover': {
5666                         fn: this.onMouseOver,
5667                         scope: this
5668                 },
5669                 'mouseout': {
5670                         fn: this.onMouseOut,
5671                         scope: this
5672                 }
5673                 });
5674                 </code></pre>
5675      * <p>
5676      * Or a shorthand syntax which passes the same scope object to all handlers:
5677         <pre><code>
5678                 el.on({
5679                         'click': this.onClick,
5680                 'mouseover': this.onMouseOver,
5681                 'mouseout': this.onMouseOut,
5682                 scope: this
5683                 });
5684                 </code></pre>
5685      */
5686     addListener : function(eventName, fn, scope, o){
5687         if(typeof eventName == "object"){
5688             o = eventName;
5689             for(var e in o){
5690                 if(this.filterOptRe.test(e)){
5691                     continue;
5692                 }
5693                 if(typeof o[e] == "function"){
5694                     // shared options
5695                     this.addListener(e, o[e], o.scope,  o);
5696                 }else{
5697                     // individual options
5698                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5699                 }
5700             }
5701             return;
5702         }
5703         o = (!o || typeof o == "boolean") ? {} : o;
5704         eventName = eventName.toLowerCase();
5705         var ce = this.events[eventName] || true;
5706         if(typeof ce == "boolean"){
5707             ce = new Roo.util.Event(this, eventName);
5708             this.events[eventName] = ce;
5709         }
5710         ce.addListener(fn, scope, o);
5711     },
5712
5713     /**
5714      * Removes a listener
5715      * @param {String}   eventName     The type of event to listen for
5716      * @param {Function} handler        The handler to remove
5717      * @param {Object}   scope  (optional) The scope (this object) for the handler
5718      */
5719     removeListener : function(eventName, fn, scope){
5720         var ce = this.events[eventName.toLowerCase()];
5721         if(typeof ce == "object"){
5722             ce.removeListener(fn, scope);
5723         }
5724     },
5725
5726     /**
5727      * Removes all listeners for this object
5728      */
5729     purgeListeners : function(){
5730         for(var evt in this.events){
5731             if(typeof this.events[evt] == "object"){
5732                  this.events[evt].clearListeners();
5733             }
5734         }
5735     },
5736
5737     relayEvents : function(o, events){
5738         var createHandler = function(ename){
5739             return function(){
5740                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5741             };
5742         };
5743         for(var i = 0, len = events.length; i < len; i++){
5744             var ename = events[i];
5745             if(!this.events[ename]){ this.events[ename] = true; };
5746             o.on(ename, createHandler(ename), this);
5747         }
5748     },
5749
5750     /**
5751      * Used to define events on this Observable
5752      * @param {Object} object The object with the events defined
5753      */
5754     addEvents : function(o){
5755         if(!this.events){
5756             this.events = {};
5757         }
5758         Roo.applyIf(this.events, o);
5759     },
5760
5761     /**
5762      * Checks to see if this object has any listeners for a specified event
5763      * @param {String} eventName The name of the event to check for
5764      * @return {Boolean} True if the event is being listened for, else false
5765      */
5766     hasListener : function(eventName){
5767         var e = this.events[eventName];
5768         return typeof e == "object" && e.listeners.length > 0;
5769     }
5770 };
5771 /**
5772  * Appends an event handler to this element (shorthand for addListener)
5773  * @param {String}   eventName     The type of event to listen for
5774  * @param {Function} handler        The method the event invokes
5775  * @param {Object}   scope (optional) The scope in which to execute the handler
5776  * function. The handler function's "this" context.
5777  * @param {Object}   options  (optional)
5778  * @method
5779  */
5780 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5781 /**
5782  * Removes a listener (shorthand for removeListener)
5783  * @param {String}   eventName     The type of event to listen for
5784  * @param {Function} handler        The handler to remove
5785  * @param {Object}   scope  (optional) The scope (this object) for the handler
5786  * @method
5787  */
5788 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5789
5790 /**
5791  * Starts capture on the specified Observable. All events will be passed
5792  * to the supplied function with the event name + standard signature of the event
5793  * <b>before</b> the event is fired. If the supplied function returns false,
5794  * the event will not fire.
5795  * @param {Observable} o The Observable to capture
5796  * @param {Function} fn The function to call
5797  * @param {Object} scope (optional) The scope (this object) for the fn
5798  * @static
5799  */
5800 Roo.util.Observable.capture = function(o, fn, scope){
5801     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5802 };
5803
5804 /**
5805  * Removes <b>all</b> added captures from the Observable.
5806  * @param {Observable} o The Observable to release
5807  * @static
5808  */
5809 Roo.util.Observable.releaseCapture = function(o){
5810     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5811 };
5812
5813 (function(){
5814
5815     var createBuffered = function(h, o, scope){
5816         var task = new Roo.util.DelayedTask();
5817         return function(){
5818             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5819         };
5820     };
5821
5822     var createSingle = function(h, e, fn, scope){
5823         return function(){
5824             e.removeListener(fn, scope);
5825             return h.apply(scope, arguments);
5826         };
5827     };
5828
5829     var createDelayed = function(h, o, scope){
5830         return function(){
5831             var args = Array.prototype.slice.call(arguments, 0);
5832             setTimeout(function(){
5833                 h.apply(scope, args);
5834             }, o.delay || 10);
5835         };
5836     };
5837
5838     Roo.util.Event = function(obj, name){
5839         this.name = name;
5840         this.obj = obj;
5841         this.listeners = [];
5842     };
5843
5844     Roo.util.Event.prototype = {
5845         addListener : function(fn, scope, options){
5846             var o = options || {};
5847             scope = scope || this.obj;
5848             if(!this.isListening(fn, scope)){
5849                 var l = {fn: fn, scope: scope, options: o};
5850                 var h = fn;
5851                 if(o.delay){
5852                     h = createDelayed(h, o, scope);
5853                 }
5854                 if(o.single){
5855                     h = createSingle(h, this, fn, scope);
5856                 }
5857                 if(o.buffer){
5858                     h = createBuffered(h, o, scope);
5859                 }
5860                 l.fireFn = h;
5861                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5862                     this.listeners.push(l);
5863                 }else{
5864                     this.listeners = this.listeners.slice(0);
5865                     this.listeners.push(l);
5866                 }
5867             }
5868         },
5869
5870         findListener : function(fn, scope){
5871             scope = scope || this.obj;
5872             var ls = this.listeners;
5873             for(var i = 0, len = ls.length; i < len; i++){
5874                 var l = ls[i];
5875                 if(l.fn == fn && l.scope == scope){
5876                     return i;
5877                 }
5878             }
5879             return -1;
5880         },
5881
5882         isListening : function(fn, scope){
5883             return this.findListener(fn, scope) != -1;
5884         },
5885
5886         removeListener : function(fn, scope){
5887             var index;
5888             if((index = this.findListener(fn, scope)) != -1){
5889                 if(!this.firing){
5890                     this.listeners.splice(index, 1);
5891                 }else{
5892                     this.listeners = this.listeners.slice(0);
5893                     this.listeners.splice(index, 1);
5894                 }
5895                 return true;
5896             }
5897             return false;
5898         },
5899
5900         clearListeners : function(){
5901             this.listeners = [];
5902         },
5903
5904         fire : function(){
5905             var ls = this.listeners, scope, len = ls.length;
5906             if(len > 0){
5907                 this.firing = true;
5908                 var args = Array.prototype.slice.call(arguments, 0);
5909                 for(var i = 0; i < len; i++){
5910                     var l = ls[i];
5911                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5912                         this.firing = false;
5913                         return false;
5914                     }
5915                 }
5916                 this.firing = false;
5917             }
5918             return true;
5919         }
5920     };
5921 })();/*
5922  * Based on:
5923  * Ext JS Library 1.1.1
5924  * Copyright(c) 2006-2007, Ext JS, LLC.
5925  *
5926  * Originally Released Under LGPL - original licence link has changed is not relivant.
5927  *
5928  * Fork - LGPL
5929  * <script type="text/javascript">
5930  */
5931
5932 /**
5933  * @class Roo.EventManager
5934  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5935  * several useful events directly.
5936  * See {@link Roo.EventObject} for more details on normalized event objects.
5937  * @singleton
5938  */
5939 Roo.EventManager = function(){
5940     var docReadyEvent, docReadyProcId, docReadyState = false;
5941     var resizeEvent, resizeTask, textEvent, textSize;
5942     var E = Roo.lib.Event;
5943     var D = Roo.lib.Dom;
5944
5945
5946     var fireDocReady = function(){
5947         if(!docReadyState){
5948             docReadyState = true;
5949             Roo.isReady = true;
5950             if(docReadyProcId){
5951                 clearInterval(docReadyProcId);
5952             }
5953             if(Roo.isGecko || Roo.isOpera) {
5954                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5955             }
5956             if(Roo.isIE){
5957                 var defer = document.getElementById("ie-deferred-loader");
5958                 if(defer){
5959                     defer.onreadystatechange = null;
5960                     defer.parentNode.removeChild(defer);
5961                 }
5962             }
5963             if(docReadyEvent){
5964                 docReadyEvent.fire();
5965                 docReadyEvent.clearListeners();
5966             }
5967         }
5968     };
5969     
5970     var initDocReady = function(){
5971         docReadyEvent = new Roo.util.Event();
5972         if(Roo.isGecko || Roo.isOpera) {
5973             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5974         }else if(Roo.isIE){
5975             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5976             var defer = document.getElementById("ie-deferred-loader");
5977             defer.onreadystatechange = function(){
5978                 if(this.readyState == "complete"){
5979                     fireDocReady();
5980                 }
5981             };
5982         }else if(Roo.isSafari){ 
5983             docReadyProcId = setInterval(function(){
5984                 var rs = document.readyState;
5985                 if(rs == "complete") {
5986                     fireDocReady();     
5987                  }
5988             }, 10);
5989         }
5990         // no matter what, make sure it fires on load
5991         E.on(window, "load", fireDocReady);
5992     };
5993
5994     var createBuffered = function(h, o){
5995         var task = new Roo.util.DelayedTask(h);
5996         return function(e){
5997             // create new event object impl so new events don't wipe out properties
5998             e = new Roo.EventObjectImpl(e);
5999             task.delay(o.buffer, h, null, [e]);
6000         };
6001     };
6002
6003     var createSingle = function(h, el, ename, fn){
6004         return function(e){
6005             Roo.EventManager.removeListener(el, ename, fn);
6006             h(e);
6007         };
6008     };
6009
6010     var createDelayed = function(h, o){
6011         return function(e){
6012             // create new event object impl so new events don't wipe out properties
6013             e = new Roo.EventObjectImpl(e);
6014             setTimeout(function(){
6015                 h(e);
6016             }, o.delay || 10);
6017         };
6018     };
6019
6020     var listen = function(element, ename, opt, fn, scope){
6021         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6022         fn = fn || o.fn; scope = scope || o.scope;
6023         var el = Roo.getDom(element);
6024         if(!el){
6025             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6026         }
6027         var h = function(e){
6028             e = Roo.EventObject.setEvent(e);
6029             var t;
6030             if(o.delegate){
6031                 t = e.getTarget(o.delegate, el);
6032                 if(!t){
6033                     return;
6034                 }
6035             }else{
6036                 t = e.target;
6037             }
6038             if(o.stopEvent === true){
6039                 e.stopEvent();
6040             }
6041             if(o.preventDefault === true){
6042                e.preventDefault();
6043             }
6044             if(o.stopPropagation === true){
6045                 e.stopPropagation();
6046             }
6047
6048             if(o.normalized === false){
6049                 e = e.browserEvent;
6050             }
6051
6052             fn.call(scope || el, e, t, o);
6053         };
6054         if(o.delay){
6055             h = createDelayed(h, o);
6056         }
6057         if(o.single){
6058             h = createSingle(h, el, ename, fn);
6059         }
6060         if(o.buffer){
6061             h = createBuffered(h, o);
6062         }
6063         fn._handlers = fn._handlers || [];
6064         fn._handlers.push([Roo.id(el), ename, h]);
6065
6066         E.on(el, ename, h);
6067         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6068             el.addEventListener("DOMMouseScroll", h, false);
6069             E.on(window, 'unload', function(){
6070                 el.removeEventListener("DOMMouseScroll", h, false);
6071             });
6072         }
6073         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6074             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6075         }
6076         return h;
6077     };
6078
6079     var stopListening = function(el, ename, fn){
6080         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6081         if(hds){
6082             for(var i = 0, len = hds.length; i < len; i++){
6083                 var h = hds[i];
6084                 if(h[0] == id && h[1] == ename){
6085                     hd = h[2];
6086                     hds.splice(i, 1);
6087                     break;
6088                 }
6089             }
6090         }
6091         E.un(el, ename, hd);
6092         el = Roo.getDom(el);
6093         if(ename == "mousewheel" && el.addEventListener){
6094             el.removeEventListener("DOMMouseScroll", hd, false);
6095         }
6096         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6097             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6098         }
6099     };
6100
6101     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6102     
6103     var pub = {
6104         
6105         
6106         /** 
6107          * Fix for doc tools
6108          * @scope Roo.EventManager
6109          */
6110         
6111         
6112         /** 
6113          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6114          * object with a Roo.EventObject
6115          * @param {Function} fn        The method the event invokes
6116          * @param {Object}   scope    An object that becomes the scope of the handler
6117          * @param {boolean}  override If true, the obj passed in becomes
6118          *                             the execution scope of the listener
6119          * @return {Function} The wrapped function
6120          * @deprecated
6121          */
6122         wrap : function(fn, scope, override){
6123             return function(e){
6124                 Roo.EventObject.setEvent(e);
6125                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6126             };
6127         },
6128         
6129         /**
6130      * Appends an event handler to an element (shorthand for addListener)
6131      * @param {String/HTMLElement}   element        The html element or id to assign the
6132      * @param {String}   eventName The type of event to listen for
6133      * @param {Function} handler The method the event invokes
6134      * @param {Object}   scope (optional) The scope in which to execute the handler
6135      * function. The handler function's "this" context.
6136      * @param {Object}   options (optional) An object containing handler configuration
6137      * properties. This may contain any of the following properties:<ul>
6138      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6139      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6140      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6141      * <li>preventDefault {Boolean} True to prevent the default action</li>
6142      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6143      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6144      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6145      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6146      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6147      * by the specified number of milliseconds. If the event fires again within that time, the original
6148      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6149      * </ul><br>
6150      * <p>
6151      * <b>Combining Options</b><br>
6152      * Using the options argument, it is possible to combine different types of listeners:<br>
6153      * <br>
6154      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6155      * Code:<pre><code>
6156 el.on('click', this.onClick, this, {
6157     single: true,
6158     delay: 100,
6159     stopEvent : true,
6160     forumId: 4
6161 });</code></pre>
6162      * <p>
6163      * <b>Attaching multiple handlers in 1 call</b><br>
6164       * The method also allows for a single argument to be passed which is a config object containing properties
6165      * which specify multiple handlers.
6166      * <p>
6167      * Code:<pre><code>
6168 el.on({
6169     'click' : {
6170         fn: this.onClick
6171         scope: this,
6172         delay: 100
6173     },
6174     'mouseover' : {
6175         fn: this.onMouseOver
6176         scope: this
6177     },
6178     'mouseout' : {
6179         fn: this.onMouseOut
6180         scope: this
6181     }
6182 });</code></pre>
6183      * <p>
6184      * Or a shorthand syntax:<br>
6185      * Code:<pre><code>
6186 el.on({
6187     'click' : this.onClick,
6188     'mouseover' : this.onMouseOver,
6189     'mouseout' : this.onMouseOut
6190     scope: this
6191 });</code></pre>
6192      */
6193         addListener : function(element, eventName, fn, scope, options){
6194             if(typeof eventName == "object"){
6195                 var o = eventName;
6196                 for(var e in o){
6197                     if(propRe.test(e)){
6198                         continue;
6199                     }
6200                     if(typeof o[e] == "function"){
6201                         // shared options
6202                         listen(element, e, o, o[e], o.scope);
6203                     }else{
6204                         // individual options
6205                         listen(element, e, o[e]);
6206                     }
6207                 }
6208                 return;
6209             }
6210             return listen(element, eventName, options, fn, scope);
6211         },
6212         
6213         /**
6214          * Removes an event handler
6215          *
6216          * @param {String/HTMLElement}   element        The id or html element to remove the 
6217          *                             event from
6218          * @param {String}   eventName     The type of event
6219          * @param {Function} fn
6220          * @return {Boolean} True if a listener was actually removed
6221          */
6222         removeListener : function(element, eventName, fn){
6223             return stopListening(element, eventName, fn);
6224         },
6225         
6226         /**
6227          * Fires when the document is ready (before onload and before images are loaded). Can be 
6228          * accessed shorthanded Roo.onReady().
6229          * @param {Function} fn        The method the event invokes
6230          * @param {Object}   scope    An  object that becomes the scope of the handler
6231          * @param {boolean}  options
6232          */
6233         onDocumentReady : function(fn, scope, options){
6234             if(docReadyState){ // if it already fired
6235                 docReadyEvent.addListener(fn, scope, options);
6236                 docReadyEvent.fire();
6237                 docReadyEvent.clearListeners();
6238                 return;
6239             }
6240             if(!docReadyEvent){
6241                 initDocReady();
6242             }
6243             docReadyEvent.addListener(fn, scope, options);
6244         },
6245         
6246         /**
6247          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6248          * @param {Function} fn        The method the event invokes
6249          * @param {Object}   scope    An object that becomes the scope of the handler
6250          * @param {boolean}  options
6251          */
6252         onWindowResize : function(fn, scope, options){
6253             if(!resizeEvent){
6254                 resizeEvent = new Roo.util.Event();
6255                 resizeTask = new Roo.util.DelayedTask(function(){
6256                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6257                 });
6258                 E.on(window, "resize", function(){
6259                     if(Roo.isIE){
6260                         resizeTask.delay(50);
6261                     }else{
6262                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6263                     }
6264                 });
6265             }
6266             resizeEvent.addListener(fn, scope, options);
6267         },
6268
6269         /**
6270          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6271          * @param {Function} fn        The method the event invokes
6272          * @param {Object}   scope    An object that becomes the scope of the handler
6273          * @param {boolean}  options
6274          */
6275         onTextResize : function(fn, scope, options){
6276             if(!textEvent){
6277                 textEvent = new Roo.util.Event();
6278                 var textEl = new Roo.Element(document.createElement('div'));
6279                 textEl.dom.className = 'x-text-resize';
6280                 textEl.dom.innerHTML = 'X';
6281                 textEl.appendTo(document.body);
6282                 textSize = textEl.dom.offsetHeight;
6283                 setInterval(function(){
6284                     if(textEl.dom.offsetHeight != textSize){
6285                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6286                     }
6287                 }, this.textResizeInterval);
6288             }
6289             textEvent.addListener(fn, scope, options);
6290         },
6291
6292         /**
6293          * Removes the passed window resize listener.
6294          * @param {Function} fn        The method the event invokes
6295          * @param {Object}   scope    The scope of handler
6296          */
6297         removeResizeListener : function(fn, scope){
6298             if(resizeEvent){
6299                 resizeEvent.removeListener(fn, scope);
6300             }
6301         },
6302
6303         // private
6304         fireResize : function(){
6305             if(resizeEvent){
6306                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6307             }   
6308         },
6309         /**
6310          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6311          */
6312         ieDeferSrc : false,
6313         /**
6314          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6315          */
6316         textResizeInterval : 50
6317     };
6318     
6319     /**
6320      * Fix for doc tools
6321      * @scopeAlias pub=Roo.EventManager
6322      */
6323     
6324      /**
6325      * Appends an event handler to an element (shorthand for addListener)
6326      * @param {String/HTMLElement}   element        The html element or id to assign the
6327      * @param {String}   eventName The type of event to listen for
6328      * @param {Function} handler The method the event invokes
6329      * @param {Object}   scope (optional) The scope in which to execute the handler
6330      * function. The handler function's "this" context.
6331      * @param {Object}   options (optional) An object containing handler configuration
6332      * properties. This may contain any of the following properties:<ul>
6333      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6334      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6335      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6336      * <li>preventDefault {Boolean} True to prevent the default action</li>
6337      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6338      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6339      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6340      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6341      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6342      * by the specified number of milliseconds. If the event fires again within that time, the original
6343      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6344      * </ul><br>
6345      * <p>
6346      * <b>Combining Options</b><br>
6347      * Using the options argument, it is possible to combine different types of listeners:<br>
6348      * <br>
6349      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6350      * Code:<pre><code>
6351 el.on('click', this.onClick, this, {
6352     single: true,
6353     delay: 100,
6354     stopEvent : true,
6355     forumId: 4
6356 });</code></pre>
6357      * <p>
6358      * <b>Attaching multiple handlers in 1 call</b><br>
6359       * The method also allows for a single argument to be passed which is a config object containing properties
6360      * which specify multiple handlers.
6361      * <p>
6362      * Code:<pre><code>
6363 el.on({
6364     'click' : {
6365         fn: this.onClick
6366         scope: this,
6367         delay: 100
6368     },
6369     'mouseover' : {
6370         fn: this.onMouseOver
6371         scope: this
6372     },
6373     'mouseout' : {
6374         fn: this.onMouseOut
6375         scope: this
6376     }
6377 });</code></pre>
6378      * <p>
6379      * Or a shorthand syntax:<br>
6380      * Code:<pre><code>
6381 el.on({
6382     'click' : this.onClick,
6383     'mouseover' : this.onMouseOver,
6384     'mouseout' : this.onMouseOut
6385     scope: this
6386 });</code></pre>
6387      */
6388     pub.on = pub.addListener;
6389     pub.un = pub.removeListener;
6390
6391     pub.stoppedMouseDownEvent = new Roo.util.Event();
6392     return pub;
6393 }();
6394 /**
6395   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6396   * @param {Function} fn        The method the event invokes
6397   * @param {Object}   scope    An  object that becomes the scope of the handler
6398   * @param {boolean}  override If true, the obj passed in becomes
6399   *                             the execution scope of the listener
6400   * @member Roo
6401   * @method onReady
6402  */
6403 Roo.onReady = Roo.EventManager.onDocumentReady;
6404
6405 Roo.onReady(function(){
6406     var bd = Roo.get(document.body);
6407     if(!bd){ return; }
6408
6409     var cls = [
6410             Roo.isIE ? "roo-ie"
6411             : Roo.isGecko ? "roo-gecko"
6412             : Roo.isOpera ? "roo-opera"
6413             : Roo.isSafari ? "roo-safari" : ""];
6414
6415     if(Roo.isMac){
6416         cls.push("roo-mac");
6417     }
6418     if(Roo.isLinux){
6419         cls.push("roo-linux");
6420     }
6421     if(Roo.isBorderBox){
6422         cls.push('roo-border-box');
6423     }
6424     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6425         var p = bd.dom.parentNode;
6426         if(p){
6427             p.className += ' roo-strict';
6428         }
6429     }
6430     bd.addClass(cls.join(' '));
6431 });
6432
6433 /**
6434  * @class Roo.EventObject
6435  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6436  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6437  * Example:
6438  * <pre><code>
6439  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6440     e.preventDefault();
6441     var target = e.getTarget();
6442     ...
6443  }
6444  var myDiv = Roo.get("myDiv");
6445  myDiv.on("click", handleClick);
6446  //or
6447  Roo.EventManager.on("myDiv", 'click', handleClick);
6448  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6449  </code></pre>
6450  * @singleton
6451  */
6452 Roo.EventObject = function(){
6453     
6454     var E = Roo.lib.Event;
6455     
6456     // safari keypress events for special keys return bad keycodes
6457     var safariKeys = {
6458         63234 : 37, // left
6459         63235 : 39, // right
6460         63232 : 38, // up
6461         63233 : 40, // down
6462         63276 : 33, // page up
6463         63277 : 34, // page down
6464         63272 : 46, // delete
6465         63273 : 36, // home
6466         63275 : 35  // end
6467     };
6468
6469     // normalize button clicks
6470     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6471                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6472
6473     Roo.EventObjectImpl = function(e){
6474         if(e){
6475             this.setEvent(e.browserEvent || e);
6476         }
6477     };
6478     Roo.EventObjectImpl.prototype = {
6479         /**
6480          * Used to fix doc tools.
6481          * @scope Roo.EventObject.prototype
6482          */
6483             
6484
6485         
6486         
6487         /** The normal browser event */
6488         browserEvent : null,
6489         /** The button pressed in a mouse event */
6490         button : -1,
6491         /** True if the shift key was down during the event */
6492         shiftKey : false,
6493         /** True if the control key was down during the event */
6494         ctrlKey : false,
6495         /** True if the alt key was down during the event */
6496         altKey : false,
6497
6498         /** Key constant 
6499         * @type Number */
6500         BACKSPACE : 8,
6501         /** Key constant 
6502         * @type Number */
6503         TAB : 9,
6504         /** Key constant 
6505         * @type Number */
6506         RETURN : 13,
6507         /** Key constant 
6508         * @type Number */
6509         ENTER : 13,
6510         /** Key constant 
6511         * @type Number */
6512         SHIFT : 16,
6513         /** Key constant 
6514         * @type Number */
6515         CONTROL : 17,
6516         /** Key constant 
6517         * @type Number */
6518         ESC : 27,
6519         /** Key constant 
6520         * @type Number */
6521         SPACE : 32,
6522         /** Key constant 
6523         * @type Number */
6524         PAGEUP : 33,
6525         /** Key constant 
6526         * @type Number */
6527         PAGEDOWN : 34,
6528         /** Key constant 
6529         * @type Number */
6530         END : 35,
6531         /** Key constant 
6532         * @type Number */
6533         HOME : 36,
6534         /** Key constant 
6535         * @type Number */
6536         LEFT : 37,
6537         /** Key constant 
6538         * @type Number */
6539         UP : 38,
6540         /** Key constant 
6541         * @type Number */
6542         RIGHT : 39,
6543         /** Key constant 
6544         * @type Number */
6545         DOWN : 40,
6546         /** Key constant 
6547         * @type Number */
6548         DELETE : 46,
6549         /** Key constant 
6550         * @type Number */
6551         F5 : 116,
6552
6553            /** @private */
6554         setEvent : function(e){
6555             if(e == this || (e && e.browserEvent)){ // already wrapped
6556                 return e;
6557             }
6558             this.browserEvent = e;
6559             if(e){
6560                 // normalize buttons
6561                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6562                 if(e.type == 'click' && this.button == -1){
6563                     this.button = 0;
6564                 }
6565                 this.type = e.type;
6566                 this.shiftKey = e.shiftKey;
6567                 // mac metaKey behaves like ctrlKey
6568                 this.ctrlKey = e.ctrlKey || e.metaKey;
6569                 this.altKey = e.altKey;
6570                 // in getKey these will be normalized for the mac
6571                 this.keyCode = e.keyCode;
6572                 // keyup warnings on firefox.
6573                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6574                 // cache the target for the delayed and or buffered events
6575                 this.target = E.getTarget(e);
6576                 // same for XY
6577                 this.xy = E.getXY(e);
6578             }else{
6579                 this.button = -1;
6580                 this.shiftKey = false;
6581                 this.ctrlKey = false;
6582                 this.altKey = false;
6583                 this.keyCode = 0;
6584                 this.charCode =0;
6585                 this.target = null;
6586                 this.xy = [0, 0];
6587             }
6588             return this;
6589         },
6590
6591         /**
6592          * Stop the event (preventDefault and stopPropagation)
6593          */
6594         stopEvent : function(){
6595             if(this.browserEvent){
6596                 if(this.browserEvent.type == 'mousedown'){
6597                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6598                 }
6599                 E.stopEvent(this.browserEvent);
6600             }
6601         },
6602
6603         /**
6604          * Prevents the browsers default handling of the event.
6605          */
6606         preventDefault : function(){
6607             if(this.browserEvent){
6608                 E.preventDefault(this.browserEvent);
6609             }
6610         },
6611
6612         /** @private */
6613         isNavKeyPress : function(){
6614             var k = this.keyCode;
6615             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6616             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6617         },
6618
6619         isSpecialKey : function(){
6620             var k = this.keyCode;
6621             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6622             (k == 16) || (k == 17) ||
6623             (k >= 18 && k <= 20) ||
6624             (k >= 33 && k <= 35) ||
6625             (k >= 36 && k <= 39) ||
6626             (k >= 44 && k <= 45);
6627         },
6628         /**
6629          * Cancels bubbling of the event.
6630          */
6631         stopPropagation : function(){
6632             if(this.browserEvent){
6633                 if(this.type == 'mousedown'){
6634                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6635                 }
6636                 E.stopPropagation(this.browserEvent);
6637             }
6638         },
6639
6640         /**
6641          * Gets the key code for the event.
6642          * @return {Number}
6643          */
6644         getCharCode : function(){
6645             return this.charCode || this.keyCode;
6646         },
6647
6648         /**
6649          * Returns a normalized keyCode for the event.
6650          * @return {Number} The key code
6651          */
6652         getKey : function(){
6653             var k = this.keyCode || this.charCode;
6654             return Roo.isSafari ? (safariKeys[k] || k) : k;
6655         },
6656
6657         /**
6658          * Gets the x coordinate of the event.
6659          * @return {Number}
6660          */
6661         getPageX : function(){
6662             return this.xy[0];
6663         },
6664
6665         /**
6666          * Gets the y coordinate of the event.
6667          * @return {Number}
6668          */
6669         getPageY : function(){
6670             return this.xy[1];
6671         },
6672
6673         /**
6674          * Gets the time of the event.
6675          * @return {Number}
6676          */
6677         getTime : function(){
6678             if(this.browserEvent){
6679                 return E.getTime(this.browserEvent);
6680             }
6681             return null;
6682         },
6683
6684         /**
6685          * Gets the page coordinates of the event.
6686          * @return {Array} The xy values like [x, y]
6687          */
6688         getXY : function(){
6689             return this.xy;
6690         },
6691
6692         /**
6693          * Gets the target for the event.
6694          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6695          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6696                 search as a number or element (defaults to 10 || document.body)
6697          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6698          * @return {HTMLelement}
6699          */
6700         getTarget : function(selector, maxDepth, returnEl){
6701             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6702         },
6703         /**
6704          * Gets the related target.
6705          * @return {HTMLElement}
6706          */
6707         getRelatedTarget : function(){
6708             if(this.browserEvent){
6709                 return E.getRelatedTarget(this.browserEvent);
6710             }
6711             return null;
6712         },
6713
6714         /**
6715          * Normalizes mouse wheel delta across browsers
6716          * @return {Number} The delta
6717          */
6718         getWheelDelta : function(){
6719             var e = this.browserEvent;
6720             var delta = 0;
6721             if(e.wheelDelta){ /* IE/Opera. */
6722                 delta = e.wheelDelta/120;
6723             }else if(e.detail){ /* Mozilla case. */
6724                 delta = -e.detail/3;
6725             }
6726             return delta;
6727         },
6728
6729         /**
6730          * Returns true if the control, meta, shift or alt key was pressed during this event.
6731          * @return {Boolean}
6732          */
6733         hasModifier : function(){
6734             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6735         },
6736
6737         /**
6738          * Returns true if the target of this event equals el or is a child of el
6739          * @param {String/HTMLElement/Element} el
6740          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6741          * @return {Boolean}
6742          */
6743         within : function(el, related){
6744             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6745             return t && Roo.fly(el).contains(t);
6746         },
6747
6748         getPoint : function(){
6749             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6750         }
6751     };
6752
6753     return new Roo.EventObjectImpl();
6754 }();
6755             
6756     /*
6757  * Based on:
6758  * Ext JS Library 1.1.1
6759  * Copyright(c) 2006-2007, Ext JS, LLC.
6760  *
6761  * Originally Released Under LGPL - original licence link has changed is not relivant.
6762  *
6763  * Fork - LGPL
6764  * <script type="text/javascript">
6765  */
6766
6767  
6768 // was in Composite Element!??!?!
6769  
6770 (function(){
6771     var D = Roo.lib.Dom;
6772     var E = Roo.lib.Event;
6773     var A = Roo.lib.Anim;
6774
6775     // local style camelizing for speed
6776     var propCache = {};
6777     var camelRe = /(-[a-z])/gi;
6778     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6779     var view = document.defaultView;
6780
6781 /**
6782  * @class Roo.Element
6783  * Represents an Element in the DOM.<br><br>
6784  * Usage:<br>
6785 <pre><code>
6786 var el = Roo.get("my-div");
6787
6788 // or with getEl
6789 var el = getEl("my-div");
6790
6791 // or with a DOM element
6792 var el = Roo.get(myDivElement);
6793 </code></pre>
6794  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6795  * each call instead of constructing a new one.<br><br>
6796  * <b>Animations</b><br />
6797  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6798  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6799 <pre>
6800 Option    Default   Description
6801 --------- --------  ---------------------------------------------
6802 duration  .35       The duration of the animation in seconds
6803 easing    easeOut   The YUI easing method
6804 callback  none      A function to execute when the anim completes
6805 scope     this      The scope (this) of the callback function
6806 </pre>
6807 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6808 * manipulate the animation. Here's an example:
6809 <pre><code>
6810 var el = Roo.get("my-div");
6811
6812 // no animation
6813 el.setWidth(100);
6814
6815 // default animation
6816 el.setWidth(100, true);
6817
6818 // animation with some options set
6819 el.setWidth(100, {
6820     duration: 1,
6821     callback: this.foo,
6822     scope: this
6823 });
6824
6825 // using the "anim" property to get the Anim object
6826 var opt = {
6827     duration: 1,
6828     callback: this.foo,
6829     scope: this
6830 };
6831 el.setWidth(100, opt);
6832 ...
6833 if(opt.anim.isAnimated()){
6834     opt.anim.stop();
6835 }
6836 </code></pre>
6837 * <b> Composite (Collections of) Elements</b><br />
6838  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6839  * @constructor Create a new Element directly.
6840  * @param {String/HTMLElement} element
6841  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6842  */
6843     Roo.Element = function(element, forceNew){
6844         var dom = typeof element == "string" ?
6845                 document.getElementById(element) : element;
6846         if(!dom){ // invalid id/element
6847             return null;
6848         }
6849         var id = dom.id;
6850         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6851             return Roo.Element.cache[id];
6852         }
6853
6854         /**
6855          * The DOM element
6856          * @type HTMLElement
6857          */
6858         this.dom = dom;
6859
6860         /**
6861          * The DOM element ID
6862          * @type String
6863          */
6864         this.id = id || Roo.id(dom);
6865     };
6866
6867     var El = Roo.Element;
6868
6869     El.prototype = {
6870         /**
6871          * The element's default display mode  (defaults to "")
6872          * @type String
6873          */
6874         originalDisplay : "",
6875
6876         visibilityMode : 1,
6877         /**
6878          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6879          * @type String
6880          */
6881         defaultUnit : "px",
6882         /**
6883          * Sets the element's visibility mode. When setVisible() is called it
6884          * will use this to determine whether to set the visibility or the display property.
6885          * @param visMode Element.VISIBILITY or Element.DISPLAY
6886          * @return {Roo.Element} this
6887          */
6888         setVisibilityMode : function(visMode){
6889             this.visibilityMode = visMode;
6890             return this;
6891         },
6892         /**
6893          * Convenience method for setVisibilityMode(Element.DISPLAY)
6894          * @param {String} display (optional) What to set display to when visible
6895          * @return {Roo.Element} this
6896          */
6897         enableDisplayMode : function(display){
6898             this.setVisibilityMode(El.DISPLAY);
6899             if(typeof display != "undefined") this.originalDisplay = display;
6900             return this;
6901         },
6902
6903         /**
6904          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6905          * @param {String} selector The simple selector to test
6906          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6907                 search as a number or element (defaults to 10 || document.body)
6908          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6909          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6910          */
6911         findParent : function(simpleSelector, maxDepth, returnEl){
6912             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6913             maxDepth = maxDepth || 50;
6914             if(typeof maxDepth != "number"){
6915                 stopEl = Roo.getDom(maxDepth);
6916                 maxDepth = 10;
6917             }
6918             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6919                 if(dq.is(p, simpleSelector)){
6920                     return returnEl ? Roo.get(p) : p;
6921                 }
6922                 depth++;
6923                 p = p.parentNode;
6924             }
6925             return null;
6926         },
6927
6928
6929         /**
6930          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6931          * @param {String} selector The simple selector to test
6932          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6933                 search as a number or element (defaults to 10 || document.body)
6934          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6935          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6936          */
6937         findParentNode : function(simpleSelector, maxDepth, returnEl){
6938             var p = Roo.fly(this.dom.parentNode, '_internal');
6939             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6940         },
6941
6942         /**
6943          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6944          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6945          * @param {String} selector The simple selector to test
6946          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6947                 search as a number or element (defaults to 10 || document.body)
6948          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6949          */
6950         up : function(simpleSelector, maxDepth){
6951             return this.findParentNode(simpleSelector, maxDepth, true);
6952         },
6953
6954
6955
6956         /**
6957          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6958          * @param {String} selector The simple selector to test
6959          * @return {Boolean} True if this element matches the selector, else false
6960          */
6961         is : function(simpleSelector){
6962             return Roo.DomQuery.is(this.dom, simpleSelector);
6963         },
6964
6965         /**
6966          * Perform animation on this element.
6967          * @param {Object} args The YUI animation control args
6968          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6969          * @param {Function} onComplete (optional) Function to call when animation completes
6970          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6971          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6972          * @return {Roo.Element} this
6973          */
6974         animate : function(args, duration, onComplete, easing, animType){
6975             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6976             return this;
6977         },
6978
6979         /*
6980          * @private Internal animation call
6981          */
6982         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6983             animType = animType || 'run';
6984             opt = opt || {};
6985             var anim = Roo.lib.Anim[animType](
6986                 this.dom, args,
6987                 (opt.duration || defaultDur) || .35,
6988                 (opt.easing || defaultEase) || 'easeOut',
6989                 function(){
6990                     Roo.callback(cb, this);
6991                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6992                 },
6993                 this
6994             );
6995             opt.anim = anim;
6996             return anim;
6997         },
6998
6999         // private legacy anim prep
7000         preanim : function(a, i){
7001             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7002         },
7003
7004         /**
7005          * Removes worthless text nodes
7006          * @param {Boolean} forceReclean (optional) By default the element
7007          * keeps track if it has been cleaned already so
7008          * you can call this over and over. However, if you update the element and
7009          * need to force a reclean, you can pass true.
7010          */
7011         clean : function(forceReclean){
7012             if(this.isCleaned && forceReclean !== true){
7013                 return this;
7014             }
7015             var ns = /\S/;
7016             var d = this.dom, n = d.firstChild, ni = -1;
7017             while(n){
7018                 var nx = n.nextSibling;
7019                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7020                     d.removeChild(n);
7021                 }else{
7022                     n.nodeIndex = ++ni;
7023                 }
7024                 n = nx;
7025             }
7026             this.isCleaned = true;
7027             return this;
7028         },
7029
7030         // private
7031         calcOffsetsTo : function(el){
7032             el = Roo.get(el);
7033             var d = el.dom;
7034             var restorePos = false;
7035             if(el.getStyle('position') == 'static'){
7036                 el.position('relative');
7037                 restorePos = true;
7038             }
7039             var x = 0, y =0;
7040             var op = this.dom;
7041             while(op && op != d && op.tagName != 'HTML'){
7042                 x+= op.offsetLeft;
7043                 y+= op.offsetTop;
7044                 op = op.offsetParent;
7045             }
7046             if(restorePos){
7047                 el.position('static');
7048             }
7049             return [x, y];
7050         },
7051
7052         /**
7053          * Scrolls this element into view within the passed container.
7054          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7055          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7056          * @return {Roo.Element} this
7057          */
7058         scrollIntoView : function(container, hscroll){
7059             var c = Roo.getDom(container) || document.body;
7060             var el = this.dom;
7061
7062             var o = this.calcOffsetsTo(c),
7063                 l = o[0],
7064                 t = o[1],
7065                 b = t+el.offsetHeight,
7066                 r = l+el.offsetWidth;
7067
7068             var ch = c.clientHeight;
7069             var ct = parseInt(c.scrollTop, 10);
7070             var cl = parseInt(c.scrollLeft, 10);
7071             var cb = ct + ch;
7072             var cr = cl + c.clientWidth;
7073
7074             if(t < ct){
7075                 c.scrollTop = t;
7076             }else if(b > cb){
7077                 c.scrollTop = b-ch;
7078             }
7079
7080             if(hscroll !== false){
7081                 if(l < cl){
7082                     c.scrollLeft = l;
7083                 }else if(r > cr){
7084                     c.scrollLeft = r-c.clientWidth;
7085                 }
7086             }
7087             return this;
7088         },
7089
7090         // private
7091         scrollChildIntoView : function(child, hscroll){
7092             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7093         },
7094
7095         /**
7096          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7097          * the new height may not be available immediately.
7098          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7099          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7100          * @param {Function} onComplete (optional) Function to call when animation completes
7101          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7102          * @return {Roo.Element} this
7103          */
7104         autoHeight : function(animate, duration, onComplete, easing){
7105             var oldHeight = this.getHeight();
7106             this.clip();
7107             this.setHeight(1); // force clipping
7108             setTimeout(function(){
7109                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7110                 if(!animate){
7111                     this.setHeight(height);
7112                     this.unclip();
7113                     if(typeof onComplete == "function"){
7114                         onComplete();
7115                     }
7116                 }else{
7117                     this.setHeight(oldHeight); // restore original height
7118                     this.setHeight(height, animate, duration, function(){
7119                         this.unclip();
7120                         if(typeof onComplete == "function") onComplete();
7121                     }.createDelegate(this), easing);
7122                 }
7123             }.createDelegate(this), 0);
7124             return this;
7125         },
7126
7127         /**
7128          * Returns true if this element is an ancestor of the passed element
7129          * @param {HTMLElement/String} el The element to check
7130          * @return {Boolean} True if this element is an ancestor of el, else false
7131          */
7132         contains : function(el){
7133             if(!el){return false;}
7134             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7135         },
7136
7137         /**
7138          * Checks whether the element is currently visible using both visibility and display properties.
7139          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7140          * @return {Boolean} True if the element is currently visible, else false
7141          */
7142         isVisible : function(deep) {
7143             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7144             if(deep !== true || !vis){
7145                 return vis;
7146             }
7147             var p = this.dom.parentNode;
7148             while(p && p.tagName.toLowerCase() != "body"){
7149                 if(!Roo.fly(p, '_isVisible').isVisible()){
7150                     return false;
7151                 }
7152                 p = p.parentNode;
7153             }
7154             return true;
7155         },
7156
7157         /**
7158          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7159          * @param {String} selector The CSS selector
7160          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7161          * @return {CompositeElement/CompositeElementLite} The composite element
7162          */
7163         select : function(selector, unique){
7164             return El.select(selector, unique, this.dom);
7165         },
7166
7167         /**
7168          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7169          * @param {String} selector The CSS selector
7170          * @return {Array} An array of the matched nodes
7171          */
7172         query : function(selector, unique){
7173             return Roo.DomQuery.select(selector, this.dom);
7174         },
7175
7176         /**
7177          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7178          * @param {String} selector The CSS selector
7179          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7180          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7181          */
7182         child : function(selector, returnDom){
7183             var n = Roo.DomQuery.selectNode(selector, this.dom);
7184             return returnDom ? n : Roo.get(n);
7185         },
7186
7187         /**
7188          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7189          * @param {String} selector The CSS selector
7190          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7191          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7192          */
7193         down : function(selector, returnDom){
7194             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7195             return returnDom ? n : Roo.get(n);
7196         },
7197
7198         /**
7199          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7200          * @param {String} group The group the DD object is member of
7201          * @param {Object} config The DD config object
7202          * @param {Object} overrides An object containing methods to override/implement on the DD object
7203          * @return {Roo.dd.DD} The DD object
7204          */
7205         initDD : function(group, config, overrides){
7206             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7207             return Roo.apply(dd, overrides);
7208         },
7209
7210         /**
7211          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7212          * @param {String} group The group the DDProxy object is member of
7213          * @param {Object} config The DDProxy config object
7214          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7215          * @return {Roo.dd.DDProxy} The DDProxy object
7216          */
7217         initDDProxy : function(group, config, overrides){
7218             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7219             return Roo.apply(dd, overrides);
7220         },
7221
7222         /**
7223          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7224          * @param {String} group The group the DDTarget object is member of
7225          * @param {Object} config The DDTarget config object
7226          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7227          * @return {Roo.dd.DDTarget} The DDTarget object
7228          */
7229         initDDTarget : function(group, config, overrides){
7230             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7231             return Roo.apply(dd, overrides);
7232         },
7233
7234         /**
7235          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7236          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7237          * @param {Boolean} visible Whether the element is visible
7238          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7239          * @return {Roo.Element} this
7240          */
7241          setVisible : function(visible, animate){
7242             if(!animate || !A){
7243                 if(this.visibilityMode == El.DISPLAY){
7244                     this.setDisplayed(visible);
7245                 }else{
7246                     this.fixDisplay();
7247                     this.dom.style.visibility = visible ? "visible" : "hidden";
7248                 }
7249             }else{
7250                 // closure for composites
7251                 var dom = this.dom;
7252                 var visMode = this.visibilityMode;
7253                 if(visible){
7254                     this.setOpacity(.01);
7255                     this.setVisible(true);
7256                 }
7257                 this.anim({opacity: { to: (visible?1:0) }},
7258                       this.preanim(arguments, 1),
7259                       null, .35, 'easeIn', function(){
7260                          if(!visible){
7261                              if(visMode == El.DISPLAY){
7262                                  dom.style.display = "none";
7263                              }else{
7264                                  dom.style.visibility = "hidden";
7265                              }
7266                              Roo.get(dom).setOpacity(1);
7267                          }
7268                      });
7269             }
7270             return this;
7271         },
7272
7273         /**
7274          * Returns true if display is not "none"
7275          * @return {Boolean}
7276          */
7277         isDisplayed : function() {
7278             return this.getStyle("display") != "none";
7279         },
7280
7281         /**
7282          * Toggles the element's visibility or display, depending on visibility mode.
7283          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7284          * @return {Roo.Element} this
7285          */
7286         toggle : function(animate){
7287             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7288             return this;
7289         },
7290
7291         /**
7292          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7293          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7294          * @return {Roo.Element} this
7295          */
7296         setDisplayed : function(value) {
7297             if(typeof value == "boolean"){
7298                value = value ? this.originalDisplay : "none";
7299             }
7300             this.setStyle("display", value);
7301             return this;
7302         },
7303
7304         /**
7305          * Tries to focus the element. Any exceptions are caught and ignored.
7306          * @return {Roo.Element} this
7307          */
7308         focus : function() {
7309             try{
7310                 this.dom.focus();
7311             }catch(e){}
7312             return this;
7313         },
7314
7315         /**
7316          * Tries to blur the element. Any exceptions are caught and ignored.
7317          * @return {Roo.Element} this
7318          */
7319         blur : function() {
7320             try{
7321                 this.dom.blur();
7322             }catch(e){}
7323             return this;
7324         },
7325
7326         /**
7327          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7328          * @param {String/Array} className The CSS class to add, or an array of classes
7329          * @return {Roo.Element} this
7330          */
7331         addClass : function(className){
7332             if(className instanceof Array){
7333                 for(var i = 0, len = className.length; i < len; i++) {
7334                     this.addClass(className[i]);
7335                 }
7336             }else{
7337                 if(className && !this.hasClass(className)){
7338                     this.dom.className = this.dom.className + " " + className;
7339                 }
7340             }
7341             return this;
7342         },
7343
7344         /**
7345          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7346          * @param {String/Array} className The CSS class to add, or an array of classes
7347          * @return {Roo.Element} this
7348          */
7349         radioClass : function(className){
7350             var siblings = this.dom.parentNode.childNodes;
7351             for(var i = 0; i < siblings.length; i++) {
7352                 var s = siblings[i];
7353                 if(s.nodeType == 1){
7354                     Roo.get(s).removeClass(className);
7355                 }
7356             }
7357             this.addClass(className);
7358             return this;
7359         },
7360
7361         /**
7362          * Removes one or more CSS classes from the element.
7363          * @param {String/Array} className The CSS class to remove, or an array of classes
7364          * @return {Roo.Element} this
7365          */
7366         removeClass : function(className){
7367             if(!className || !this.dom.className){
7368                 return this;
7369             }
7370             if(className instanceof Array){
7371                 for(var i = 0, len = className.length; i < len; i++) {
7372                     this.removeClass(className[i]);
7373                 }
7374             }else{
7375                 if(this.hasClass(className)){
7376                     var re = this.classReCache[className];
7377                     if (!re) {
7378                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7379                        this.classReCache[className] = re;
7380                     }
7381                     this.dom.className =
7382                         this.dom.className.replace(re, " ");
7383                 }
7384             }
7385             return this;
7386         },
7387
7388         // private
7389         classReCache: {},
7390
7391         /**
7392          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7393          * @param {String} className The CSS class to toggle
7394          * @return {Roo.Element} this
7395          */
7396         toggleClass : function(className){
7397             if(this.hasClass(className)){
7398                 this.removeClass(className);
7399             }else{
7400                 this.addClass(className);
7401             }
7402             return this;
7403         },
7404
7405         /**
7406          * Checks if the specified CSS class exists on this element's DOM node.
7407          * @param {String} className The CSS class to check for
7408          * @return {Boolean} True if the class exists, else false
7409          */
7410         hasClass : function(className){
7411             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7412         },
7413
7414         /**
7415          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7416          * @param {String} oldClassName The CSS class to replace
7417          * @param {String} newClassName The replacement CSS class
7418          * @return {Roo.Element} this
7419          */
7420         replaceClass : function(oldClassName, newClassName){
7421             this.removeClass(oldClassName);
7422             this.addClass(newClassName);
7423             return this;
7424         },
7425
7426         /**
7427          * Returns an object with properties matching the styles requested.
7428          * For example, el.getStyles('color', 'font-size', 'width') might return
7429          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7430          * @param {String} style1 A style name
7431          * @param {String} style2 A style name
7432          * @param {String} etc.
7433          * @return {Object} The style object
7434          */
7435         getStyles : function(){
7436             var a = arguments, len = a.length, r = {};
7437             for(var i = 0; i < len; i++){
7438                 r[a[i]] = this.getStyle(a[i]);
7439             }
7440             return r;
7441         },
7442
7443         /**
7444          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7445          * @param {String} property The style property whose value is returned.
7446          * @return {String} The current value of the style property for this element.
7447          */
7448         getStyle : function(){
7449             return view && view.getComputedStyle ?
7450                 function(prop){
7451                     var el = this.dom, v, cs, camel;
7452                     if(prop == 'float'){
7453                         prop = "cssFloat";
7454                     }
7455                     if(el.style && (v = el.style[prop])){
7456                         return v;
7457                     }
7458                     if(cs = view.getComputedStyle(el, "")){
7459                         if(!(camel = propCache[prop])){
7460                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7461                         }
7462                         return cs[camel];
7463                     }
7464                     return null;
7465                 } :
7466                 function(prop){
7467                     var el = this.dom, v, cs, camel;
7468                     if(prop == 'opacity'){
7469                         if(typeof el.style.filter == 'string'){
7470                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7471                             if(m){
7472                                 var fv = parseFloat(m[1]);
7473                                 if(!isNaN(fv)){
7474                                     return fv ? fv / 100 : 0;
7475                                 }
7476                             }
7477                         }
7478                         return 1;
7479                     }else if(prop == 'float'){
7480                         prop = "styleFloat";
7481                     }
7482                     if(!(camel = propCache[prop])){
7483                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7484                     }
7485                     if(v = el.style[camel]){
7486                         return v;
7487                     }
7488                     if(cs = el.currentStyle){
7489                         return cs[camel];
7490                     }
7491                     return null;
7492                 };
7493         }(),
7494
7495         /**
7496          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7497          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7498          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7499          * @return {Roo.Element} this
7500          */
7501         setStyle : function(prop, value){
7502             if(typeof prop == "string"){
7503                 
7504                 if (prop == 'float') {
7505                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7506                     return this;
7507                 }
7508                 
7509                 var camel;
7510                 if(!(camel = propCache[prop])){
7511                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7512                 }
7513                 
7514                 if(camel == 'opacity') {
7515                     this.setOpacity(value);
7516                 }else{
7517                     this.dom.style[camel] = value;
7518                 }
7519             }else{
7520                 for(var style in prop){
7521                     if(typeof prop[style] != "function"){
7522                        this.setStyle(style, prop[style]);
7523                     }
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         /**
7530          * More flexible version of {@link #setStyle} for setting style properties.
7531          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7532          * a function which returns such a specification.
7533          * @return {Roo.Element} this
7534          */
7535         applyStyles : function(style){
7536             Roo.DomHelper.applyStyles(this.dom, style);
7537             return this;
7538         },
7539
7540         /**
7541           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7542           * @return {Number} The X position of the element
7543           */
7544         getX : function(){
7545             return D.getX(this.dom);
7546         },
7547
7548         /**
7549           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7550           * @return {Number} The Y position of the element
7551           */
7552         getY : function(){
7553             return D.getY(this.dom);
7554         },
7555
7556         /**
7557           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7558           * @return {Array} The XY position of the element
7559           */
7560         getXY : function(){
7561             return D.getXY(this.dom);
7562         },
7563
7564         /**
7565          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7566          * @param {Number} The X position of the element
7567          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568          * @return {Roo.Element} this
7569          */
7570         setX : function(x, animate){
7571             if(!animate || !A){
7572                 D.setX(this.dom, x);
7573             }else{
7574                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7575             }
7576             return this;
7577         },
7578
7579         /**
7580          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7581          * @param {Number} The Y position of the element
7582          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7583          * @return {Roo.Element} this
7584          */
7585         setY : function(y, animate){
7586             if(!animate || !A){
7587                 D.setY(this.dom, y);
7588             }else{
7589                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7590             }
7591             return this;
7592         },
7593
7594         /**
7595          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7596          * @param {String} left The left CSS property value
7597          * @return {Roo.Element} this
7598          */
7599         setLeft : function(left){
7600             this.setStyle("left", this.addUnits(left));
7601             return this;
7602         },
7603
7604         /**
7605          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7606          * @param {String} top The top CSS property value
7607          * @return {Roo.Element} this
7608          */
7609         setTop : function(top){
7610             this.setStyle("top", this.addUnits(top));
7611             return this;
7612         },
7613
7614         /**
7615          * Sets the element's CSS right style.
7616          * @param {String} right The right CSS property value
7617          * @return {Roo.Element} this
7618          */
7619         setRight : function(right){
7620             this.setStyle("right", this.addUnits(right));
7621             return this;
7622         },
7623
7624         /**
7625          * Sets the element's CSS bottom style.
7626          * @param {String} bottom The bottom CSS property value
7627          * @return {Roo.Element} this
7628          */
7629         setBottom : function(bottom){
7630             this.setStyle("bottom", this.addUnits(bottom));
7631             return this;
7632         },
7633
7634         /**
7635          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7636          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7637          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7638          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7639          * @return {Roo.Element} this
7640          */
7641         setXY : function(pos, animate){
7642             if(!animate || !A){
7643                 D.setXY(this.dom, pos);
7644             }else{
7645                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7646             }
7647             return this;
7648         },
7649
7650         /**
7651          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7652          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7653          * @param {Number} x X value for new position (coordinates are page-based)
7654          * @param {Number} y Y value for new position (coordinates are page-based)
7655          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7656          * @return {Roo.Element} this
7657          */
7658         setLocation : function(x, y, animate){
7659             this.setXY([x, y], this.preanim(arguments, 2));
7660             return this;
7661         },
7662
7663         /**
7664          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7665          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7666          * @param {Number} x X value for new position (coordinates are page-based)
7667          * @param {Number} y Y value for new position (coordinates are page-based)
7668          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7669          * @return {Roo.Element} this
7670          */
7671         moveTo : function(x, y, animate){
7672             this.setXY([x, y], this.preanim(arguments, 2));
7673             return this;
7674         },
7675
7676         /**
7677          * Returns the region of the given element.
7678          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7679          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7680          */
7681         getRegion : function(){
7682             return D.getRegion(this.dom);
7683         },
7684
7685         /**
7686          * Returns the offset height of the element
7687          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7688          * @return {Number} The element's height
7689          */
7690         getHeight : function(contentHeight){
7691             var h = this.dom.offsetHeight || 0;
7692             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7693         },
7694
7695         /**
7696          * Returns the offset width of the element
7697          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7698          * @return {Number} The element's width
7699          */
7700         getWidth : function(contentWidth){
7701             var w = this.dom.offsetWidth || 0;
7702             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7703         },
7704
7705         /**
7706          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7707          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7708          * if a height has not been set using CSS.
7709          * @return {Number}
7710          */
7711         getComputedHeight : function(){
7712             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7713             if(!h){
7714                 h = parseInt(this.getStyle('height'), 10) || 0;
7715                 if(!this.isBorderBox()){
7716                     h += this.getFrameWidth('tb');
7717                 }
7718             }
7719             return h;
7720         },
7721
7722         /**
7723          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7724          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7725          * if a width has not been set using CSS.
7726          * @return {Number}
7727          */
7728         getComputedWidth : function(){
7729             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7730             if(!w){
7731                 w = parseInt(this.getStyle('width'), 10) || 0;
7732                 if(!this.isBorderBox()){
7733                     w += this.getFrameWidth('lr');
7734                 }
7735             }
7736             return w;
7737         },
7738
7739         /**
7740          * Returns the size of the element.
7741          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7742          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7743          */
7744         getSize : function(contentSize){
7745             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7746         },
7747
7748         /**
7749          * Returns the width and height of the viewport.
7750          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7751          */
7752         getViewSize : function(){
7753             var d = this.dom, doc = document, aw = 0, ah = 0;
7754             if(d == doc || d == doc.body){
7755                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7756             }else{
7757                 return {
7758                     width : d.clientWidth,
7759                     height: d.clientHeight
7760                 };
7761             }
7762         },
7763
7764         /**
7765          * Returns the value of the "value" attribute
7766          * @param {Boolean} asNumber true to parse the value as a number
7767          * @return {String/Number}
7768          */
7769         getValue : function(asNumber){
7770             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7771         },
7772
7773         // private
7774         adjustWidth : function(width){
7775             if(typeof width == "number"){
7776                 if(this.autoBoxAdjust && !this.isBorderBox()){
7777                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7778                 }
7779                 if(width < 0){
7780                     width = 0;
7781                 }
7782             }
7783             return width;
7784         },
7785
7786         // private
7787         adjustHeight : function(height){
7788             if(typeof height == "number"){
7789                if(this.autoBoxAdjust && !this.isBorderBox()){
7790                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7791                }
7792                if(height < 0){
7793                    height = 0;
7794                }
7795             }
7796             return height;
7797         },
7798
7799         /**
7800          * Set the width of the element
7801          * @param {Number} width The new width
7802          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7803          * @return {Roo.Element} this
7804          */
7805         setWidth : function(width, animate){
7806             width = this.adjustWidth(width);
7807             if(!animate || !A){
7808                 this.dom.style.width = this.addUnits(width);
7809             }else{
7810                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7811             }
7812             return this;
7813         },
7814
7815         /**
7816          * Set the height of the element
7817          * @param {Number} height The new height
7818          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7819          * @return {Roo.Element} this
7820          */
7821          setHeight : function(height, animate){
7822             height = this.adjustHeight(height);
7823             if(!animate || !A){
7824                 this.dom.style.height = this.addUnits(height);
7825             }else{
7826                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7827             }
7828             return this;
7829         },
7830
7831         /**
7832          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7833          * @param {Number} width The new width
7834          * @param {Number} height The new height
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838          setSize : function(width, height, animate){
7839             if(typeof width == "object"){ // in case of object from getSize()
7840                 height = width.height; width = width.width;
7841             }
7842             width = this.adjustWidth(width); height = this.adjustHeight(height);
7843             if(!animate || !A){
7844                 this.dom.style.width = this.addUnits(width);
7845                 this.dom.style.height = this.addUnits(height);
7846             }else{
7847                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7848             }
7849             return this;
7850         },
7851
7852         /**
7853          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7854          * @param {Number} x X value for new position (coordinates are page-based)
7855          * @param {Number} y Y value for new position (coordinates are page-based)
7856          * @param {Number} width The new width
7857          * @param {Number} height The new height
7858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setBounds : function(x, y, width, height, animate){
7862             if(!animate || !A){
7863                 this.setSize(width, height);
7864                 this.setLocation(x, y);
7865             }else{
7866                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7867                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7868                               this.preanim(arguments, 4), 'motion');
7869             }
7870             return this;
7871         },
7872
7873         /**
7874          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7875          * @param {Roo.lib.Region} region The region to fill
7876          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7877          * @return {Roo.Element} this
7878          */
7879         setRegion : function(region, animate){
7880             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7881             return this;
7882         },
7883
7884         /**
7885          * Appends an event handler
7886          *
7887          * @param {String}   eventName     The type of event to append
7888          * @param {Function} fn        The method the event invokes
7889          * @param {Object} scope       (optional) The scope (this object) of the fn
7890          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7891          */
7892         addListener : function(eventName, fn, scope, options){
7893             if (this.dom) {
7894                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7895             }
7896         },
7897
7898         /**
7899          * Removes an event handler from this element
7900          * @param {String} eventName the type of event to remove
7901          * @param {Function} fn the method the event invokes
7902          * @return {Roo.Element} this
7903          */
7904         removeListener : function(eventName, fn){
7905             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7906             return this;
7907         },
7908
7909         /**
7910          * Removes all previous added listeners from this element
7911          * @return {Roo.Element} this
7912          */
7913         removeAllListeners : function(){
7914             E.purgeElement(this.dom);
7915             return this;
7916         },
7917
7918         relayEvent : function(eventName, observable){
7919             this.on(eventName, function(e){
7920                 observable.fireEvent(eventName, e);
7921             });
7922         },
7923
7924         /**
7925          * Set the opacity of the element
7926          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7927          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7928          * @return {Roo.Element} this
7929          */
7930          setOpacity : function(opacity, animate){
7931             if(!animate || !A){
7932                 var s = this.dom.style;
7933                 if(Roo.isIE){
7934                     s.zoom = 1;
7935                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7936                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7937                 }else{
7938                     s.opacity = opacity;
7939                 }
7940             }else{
7941                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7942             }
7943             return this;
7944         },
7945
7946         /**
7947          * Gets the left X coordinate
7948          * @param {Boolean} local True to get the local css position instead of page coordinate
7949          * @return {Number}
7950          */
7951         getLeft : function(local){
7952             if(!local){
7953                 return this.getX();
7954             }else{
7955                 return parseInt(this.getStyle("left"), 10) || 0;
7956             }
7957         },
7958
7959         /**
7960          * Gets the right X coordinate of the element (element X position + element width)
7961          * @param {Boolean} local True to get the local css position instead of page coordinate
7962          * @return {Number}
7963          */
7964         getRight : function(local){
7965             if(!local){
7966                 return this.getX() + this.getWidth();
7967             }else{
7968                 return (this.getLeft(true) + this.getWidth()) || 0;
7969             }
7970         },
7971
7972         /**
7973          * Gets the top Y coordinate
7974          * @param {Boolean} local True to get the local css position instead of page coordinate
7975          * @return {Number}
7976          */
7977         getTop : function(local) {
7978             if(!local){
7979                 return this.getY();
7980             }else{
7981                 return parseInt(this.getStyle("top"), 10) || 0;
7982             }
7983         },
7984
7985         /**
7986          * Gets the bottom Y coordinate of the element (element Y position + element height)
7987          * @param {Boolean} local True to get the local css position instead of page coordinate
7988          * @return {Number}
7989          */
7990         getBottom : function(local){
7991             if(!local){
7992                 return this.getY() + this.getHeight();
7993             }else{
7994                 return (this.getTop(true) + this.getHeight()) || 0;
7995             }
7996         },
7997
7998         /**
7999         * Initializes positioning on this element. If a desired position is not passed, it will make the
8000         * the element positioned relative IF it is not already positioned.
8001         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8002         * @param {Number} zIndex (optional) The zIndex to apply
8003         * @param {Number} x (optional) Set the page X position
8004         * @param {Number} y (optional) Set the page Y position
8005         */
8006         position : function(pos, zIndex, x, y){
8007             if(!pos){
8008                if(this.getStyle('position') == 'static'){
8009                    this.setStyle('position', 'relative');
8010                }
8011             }else{
8012                 this.setStyle("position", pos);
8013             }
8014             if(zIndex){
8015                 this.setStyle("z-index", zIndex);
8016             }
8017             if(x !== undefined && y !== undefined){
8018                 this.setXY([x, y]);
8019             }else if(x !== undefined){
8020                 this.setX(x);
8021             }else if(y !== undefined){
8022                 this.setY(y);
8023             }
8024         },
8025
8026         /**
8027         * Clear positioning back to the default when the document was loaded
8028         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8029         * @return {Roo.Element} this
8030          */
8031         clearPositioning : function(value){
8032             value = value ||'';
8033             this.setStyle({
8034                 "left": value,
8035                 "right": value,
8036                 "top": value,
8037                 "bottom": value,
8038                 "z-index": "",
8039                 "position" : "static"
8040             });
8041             return this;
8042         },
8043
8044         /**
8045         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8046         * snapshot before performing an update and then restoring the element.
8047         * @return {Object}
8048         */
8049         getPositioning : function(){
8050             var l = this.getStyle("left");
8051             var t = this.getStyle("top");
8052             return {
8053                 "position" : this.getStyle("position"),
8054                 "left" : l,
8055                 "right" : l ? "" : this.getStyle("right"),
8056                 "top" : t,
8057                 "bottom" : t ? "" : this.getStyle("bottom"),
8058                 "z-index" : this.getStyle("z-index")
8059             };
8060         },
8061
8062         /**
8063          * Gets the width of the border(s) for the specified side(s)
8064          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8065          * passing lr would get the border (l)eft width + the border (r)ight width.
8066          * @return {Number} The width of the sides passed added together
8067          */
8068         getBorderWidth : function(side){
8069             return this.addStyles(side, El.borders);
8070         },
8071
8072         /**
8073          * Gets the width of the padding(s) for the specified side(s)
8074          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8075          * passing lr would get the padding (l)eft + the padding (r)ight.
8076          * @return {Number} The padding of the sides passed added together
8077          */
8078         getPadding : function(side){
8079             return this.addStyles(side, El.paddings);
8080         },
8081
8082         /**
8083         * Set positioning with an object returned by getPositioning().
8084         * @param {Object} posCfg
8085         * @return {Roo.Element} this
8086          */
8087         setPositioning : function(pc){
8088             this.applyStyles(pc);
8089             if(pc.right == "auto"){
8090                 this.dom.style.right = "";
8091             }
8092             if(pc.bottom == "auto"){
8093                 this.dom.style.bottom = "";
8094             }
8095             return this;
8096         },
8097
8098         // private
8099         fixDisplay : function(){
8100             if(this.getStyle("display") == "none"){
8101                 this.setStyle("visibility", "hidden");
8102                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8103                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8104                     this.setStyle("display", "block");
8105                 }
8106             }
8107         },
8108
8109         /**
8110          * Quick set left and top adding default units
8111          * @param {String} left The left CSS property value
8112          * @param {String} top The top CSS property value
8113          * @return {Roo.Element} this
8114          */
8115          setLeftTop : function(left, top){
8116             this.dom.style.left = this.addUnits(left);
8117             this.dom.style.top = this.addUnits(top);
8118             return this;
8119         },
8120
8121         /**
8122          * Move this element relative to its current position.
8123          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8124          * @param {Number} distance How far to move the element in pixels
8125          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8126          * @return {Roo.Element} this
8127          */
8128          move : function(direction, distance, animate){
8129             var xy = this.getXY();
8130             direction = direction.toLowerCase();
8131             switch(direction){
8132                 case "l":
8133                 case "left":
8134                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8135                     break;
8136                case "r":
8137                case "right":
8138                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8139                     break;
8140                case "t":
8141                case "top":
8142                case "up":
8143                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8144                     break;
8145                case "b":
8146                case "bottom":
8147                case "down":
8148                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8149                     break;
8150             }
8151             return this;
8152         },
8153
8154         /**
8155          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8156          * @return {Roo.Element} this
8157          */
8158         clip : function(){
8159             if(!this.isClipped){
8160                this.isClipped = true;
8161                this.originalClip = {
8162                    "o": this.getStyle("overflow"),
8163                    "x": this.getStyle("overflow-x"),
8164                    "y": this.getStyle("overflow-y")
8165                };
8166                this.setStyle("overflow", "hidden");
8167                this.setStyle("overflow-x", "hidden");
8168                this.setStyle("overflow-y", "hidden");
8169             }
8170             return this;
8171         },
8172
8173         /**
8174          *  Return clipping (overflow) to original clipping before clip() was called
8175          * @return {Roo.Element} this
8176          */
8177         unclip : function(){
8178             if(this.isClipped){
8179                 this.isClipped = false;
8180                 var o = this.originalClip;
8181                 if(o.o){this.setStyle("overflow", o.o);}
8182                 if(o.x){this.setStyle("overflow-x", o.x);}
8183                 if(o.y){this.setStyle("overflow-y", o.y);}
8184             }
8185             return this;
8186         },
8187
8188
8189         /**
8190          * Gets the x,y coordinates specified by the anchor position on the element.
8191          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8192          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8193          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8194          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8195          * @return {Array} [x, y] An array containing the element's x and y coordinates
8196          */
8197         getAnchorXY : function(anchor, local, s){
8198             //Passing a different size is useful for pre-calculating anchors,
8199             //especially for anchored animations that change the el size.
8200
8201             var w, h, vp = false;
8202             if(!s){
8203                 var d = this.dom;
8204                 if(d == document.body || d == document){
8205                     vp = true;
8206                     w = D.getViewWidth(); h = D.getViewHeight();
8207                 }else{
8208                     w = this.getWidth(); h = this.getHeight();
8209                 }
8210             }else{
8211                 w = s.width;  h = s.height;
8212             }
8213             var x = 0, y = 0, r = Math.round;
8214             switch((anchor || "tl").toLowerCase()){
8215                 case "c":
8216                     x = r(w*.5);
8217                     y = r(h*.5);
8218                 break;
8219                 case "t":
8220                     x = r(w*.5);
8221                     y = 0;
8222                 break;
8223                 case "l":
8224                     x = 0;
8225                     y = r(h*.5);
8226                 break;
8227                 case "r":
8228                     x = w;
8229                     y = r(h*.5);
8230                 break;
8231                 case "b":
8232                     x = r(w*.5);
8233                     y = h;
8234                 break;
8235                 case "tl":
8236                     x = 0;
8237                     y = 0;
8238                 break;
8239                 case "bl":
8240                     x = 0;
8241                     y = h;
8242                 break;
8243                 case "br":
8244                     x = w;
8245                     y = h;
8246                 break;
8247                 case "tr":
8248                     x = w;
8249                     y = 0;
8250                 break;
8251             }
8252             if(local === true){
8253                 return [x, y];
8254             }
8255             if(vp){
8256                 var sc = this.getScroll();
8257                 return [x + sc.left, y + sc.top];
8258             }
8259             //Add the element's offset xy
8260             var o = this.getXY();
8261             return [x+o[0], y+o[1]];
8262         },
8263
8264         /**
8265          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8266          * supported position values.
8267          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8268          * @param {String} position The position to align to.
8269          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8270          * @return {Array} [x, y]
8271          */
8272         getAlignToXY : function(el, p, o){
8273             el = Roo.get(el);
8274             var d = this.dom;
8275             if(!el.dom){
8276                 throw "Element.alignTo with an element that doesn't exist";
8277             }
8278             var c = false; //constrain to viewport
8279             var p1 = "", p2 = "";
8280             o = o || [0,0];
8281
8282             if(!p){
8283                 p = "tl-bl";
8284             }else if(p == "?"){
8285                 p = "tl-bl?";
8286             }else if(p.indexOf("-") == -1){
8287                 p = "tl-" + p;
8288             }
8289             p = p.toLowerCase();
8290             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8291             if(!m){
8292                throw "Element.alignTo with an invalid alignment " + p;
8293             }
8294             p1 = m[1]; p2 = m[2]; c = !!m[3];
8295
8296             //Subtract the aligned el's internal xy from the target's offset xy
8297             //plus custom offset to get the aligned el's new offset xy
8298             var a1 = this.getAnchorXY(p1, true);
8299             var a2 = el.getAnchorXY(p2, false);
8300             var x = a2[0] - a1[0] + o[0];
8301             var y = a2[1] - a1[1] + o[1];
8302             if(c){
8303                 //constrain the aligned el to viewport if necessary
8304                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8305                 // 5px of margin for ie
8306                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8307
8308                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8309                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8310                 //otherwise swap the aligned el to the opposite border of the target.
8311                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8312                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8313                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8314                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8315
8316                var doc = document;
8317                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8318                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8319
8320                if((x+w) > dw + scrollX){
8321                     x = swapX ? r.left-w : dw+scrollX-w;
8322                 }
8323                if(x < scrollX){
8324                    x = swapX ? r.right : scrollX;
8325                }
8326                if((y+h) > dh + scrollY){
8327                     y = swapY ? r.top-h : dh+scrollY-h;
8328                 }
8329                if (y < scrollY){
8330                    y = swapY ? r.bottom : scrollY;
8331                }
8332             }
8333             return [x,y];
8334         },
8335
8336         // private
8337         getConstrainToXY : function(){
8338             var os = {top:0, left:0, bottom:0, right: 0};
8339
8340             return function(el, local, offsets, proposedXY){
8341                 el = Roo.get(el);
8342                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8343
8344                 var vw, vh, vx = 0, vy = 0;
8345                 if(el.dom == document.body || el.dom == document){
8346                     vw = Roo.lib.Dom.getViewWidth();
8347                     vh = Roo.lib.Dom.getViewHeight();
8348                 }else{
8349                     vw = el.dom.clientWidth;
8350                     vh = el.dom.clientHeight;
8351                     if(!local){
8352                         var vxy = el.getXY();
8353                         vx = vxy[0];
8354                         vy = vxy[1];
8355                     }
8356                 }
8357
8358                 var s = el.getScroll();
8359
8360                 vx += offsets.left + s.left;
8361                 vy += offsets.top + s.top;
8362
8363                 vw -= offsets.right;
8364                 vh -= offsets.bottom;
8365
8366                 var vr = vx+vw;
8367                 var vb = vy+vh;
8368
8369                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8370                 var x = xy[0], y = xy[1];
8371                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8372
8373                 // only move it if it needs it
8374                 var moved = false;
8375
8376                 // first validate right/bottom
8377                 if((x + w) > vr){
8378                     x = vr - w;
8379                     moved = true;
8380                 }
8381                 if((y + h) > vb){
8382                     y = vb - h;
8383                     moved = true;
8384                 }
8385                 // then make sure top/left isn't negative
8386                 if(x < vx){
8387                     x = vx;
8388                     moved = true;
8389                 }
8390                 if(y < vy){
8391                     y = vy;
8392                     moved = true;
8393                 }
8394                 return moved ? [x, y] : false;
8395             };
8396         }(),
8397
8398         // private
8399         adjustForConstraints : function(xy, parent, offsets){
8400             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8401         },
8402
8403         /**
8404          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8405          * document it aligns it to the viewport.
8406          * The position parameter is optional, and can be specified in any one of the following formats:
8407          * <ul>
8408          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8409          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8410          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8411          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8412          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8413          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8414          * </ul>
8415          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8416          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8417          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8418          * that specified in order to enforce the viewport constraints.
8419          * Following are all of the supported anchor positions:
8420     <pre>
8421     Value  Description
8422     -----  -----------------------------
8423     tl     The top left corner (default)
8424     t      The center of the top edge
8425     tr     The top right corner
8426     l      The center of the left edge
8427     c      In the center of the element
8428     r      The center of the right edge
8429     bl     The bottom left corner
8430     b      The center of the bottom edge
8431     br     The bottom right corner
8432     </pre>
8433     Example Usage:
8434     <pre><code>
8435     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8436     el.alignTo("other-el");
8437
8438     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8439     el.alignTo("other-el", "tr?");
8440
8441     // align the bottom right corner of el with the center left edge of other-el
8442     el.alignTo("other-el", "br-l?");
8443
8444     // align the center of el with the bottom left corner of other-el and
8445     // adjust the x position by -6 pixels (and the y position by 0)
8446     el.alignTo("other-el", "c-bl", [-6, 0]);
8447     </code></pre>
8448          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8449          * @param {String} position The position to align to.
8450          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8451          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8452          * @return {Roo.Element} this
8453          */
8454         alignTo : function(element, position, offsets, animate){
8455             var xy = this.getAlignToXY(element, position, offsets);
8456             this.setXY(xy, this.preanim(arguments, 3));
8457             return this;
8458         },
8459
8460         /**
8461          * Anchors an element to another element and realigns it when the window is resized.
8462          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8463          * @param {String} position The position to align to.
8464          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8465          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8466          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8467          * is a number, it is used as the buffer delay (defaults to 50ms).
8468          * @param {Function} callback The function to call after the animation finishes
8469          * @return {Roo.Element} this
8470          */
8471         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8472             var action = function(){
8473                 this.alignTo(el, alignment, offsets, animate);
8474                 Roo.callback(callback, this);
8475             };
8476             Roo.EventManager.onWindowResize(action, this);
8477             var tm = typeof monitorScroll;
8478             if(tm != 'undefined'){
8479                 Roo.EventManager.on(window, 'scroll', action, this,
8480                     {buffer: tm == 'number' ? monitorScroll : 50});
8481             }
8482             action.call(this); // align immediately
8483             return this;
8484         },
8485         /**
8486          * Clears any opacity settings from this element. Required in some cases for IE.
8487          * @return {Roo.Element} this
8488          */
8489         clearOpacity : function(){
8490             if (window.ActiveXObject) {
8491                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8492                     this.dom.style.filter = "";
8493                 }
8494             } else {
8495                 this.dom.style.opacity = "";
8496                 this.dom.style["-moz-opacity"] = "";
8497                 this.dom.style["-khtml-opacity"] = "";
8498             }
8499             return this;
8500         },
8501
8502         /**
8503          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8504          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8505          * @return {Roo.Element} this
8506          */
8507         hide : function(animate){
8508             this.setVisible(false, this.preanim(arguments, 0));
8509             return this;
8510         },
8511
8512         /**
8513         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8514         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8515          * @return {Roo.Element} this
8516          */
8517         show : function(animate){
8518             this.setVisible(true, this.preanim(arguments, 0));
8519             return this;
8520         },
8521
8522         /**
8523          * @private Test if size has a unit, otherwise appends the default
8524          */
8525         addUnits : function(size){
8526             return Roo.Element.addUnits(size, this.defaultUnit);
8527         },
8528
8529         /**
8530          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8531          * @return {Roo.Element} this
8532          */
8533         beginMeasure : function(){
8534             var el = this.dom;
8535             if(el.offsetWidth || el.offsetHeight){
8536                 return this; // offsets work already
8537             }
8538             var changed = [];
8539             var p = this.dom, b = document.body; // start with this element
8540             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8541                 var pe = Roo.get(p);
8542                 if(pe.getStyle('display') == 'none'){
8543                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8544                     p.style.visibility = "hidden";
8545                     p.style.display = "block";
8546                 }
8547                 p = p.parentNode;
8548             }
8549             this._measureChanged = changed;
8550             return this;
8551
8552         },
8553
8554         /**
8555          * Restores displays to before beginMeasure was called
8556          * @return {Roo.Element} this
8557          */
8558         endMeasure : function(){
8559             var changed = this._measureChanged;
8560             if(changed){
8561                 for(var i = 0, len = changed.length; i < len; i++) {
8562                     var r = changed[i];
8563                     r.el.style.visibility = r.visibility;
8564                     r.el.style.display = "none";
8565                 }
8566                 this._measureChanged = null;
8567             }
8568             return this;
8569         },
8570
8571         /**
8572         * Update the innerHTML of this element, optionally searching for and processing scripts
8573         * @param {String} html The new HTML
8574         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8575         * @param {Function} callback For async script loading you can be noticed when the update completes
8576         * @return {Roo.Element} this
8577          */
8578         update : function(html, loadScripts, callback){
8579             if(typeof html == "undefined"){
8580                 html = "";
8581             }
8582             if(loadScripts !== true){
8583                 this.dom.innerHTML = html;
8584                 if(typeof callback == "function"){
8585                     callback();
8586                 }
8587                 return this;
8588             }
8589             var id = Roo.id();
8590             var dom = this.dom;
8591
8592             html += '<span id="' + id + '"></span>';
8593
8594             E.onAvailable(id, function(){
8595                 var hd = document.getElementsByTagName("head")[0];
8596                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8597                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8598                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8599
8600                 var match;
8601                 while(match = re.exec(html)){
8602                     var attrs = match[1];
8603                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8604                     if(srcMatch && srcMatch[2]){
8605                        var s = document.createElement("script");
8606                        s.src = srcMatch[2];
8607                        var typeMatch = attrs.match(typeRe);
8608                        if(typeMatch && typeMatch[2]){
8609                            s.type = typeMatch[2];
8610                        }
8611                        hd.appendChild(s);
8612                     }else if(match[2] && match[2].length > 0){
8613                         if(window.execScript) {
8614                            window.execScript(match[2]);
8615                         } else {
8616                             /**
8617                              * eval:var:id
8618                              * eval:var:dom
8619                              * eval:var:html
8620                              * 
8621                              */
8622                            window.eval(match[2]);
8623                         }
8624                     }
8625                 }
8626                 var el = document.getElementById(id);
8627                 if(el){el.parentNode.removeChild(el);}
8628                 if(typeof callback == "function"){
8629                     callback();
8630                 }
8631             });
8632             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8633             return this;
8634         },
8635
8636         /**
8637          * Direct access to the UpdateManager update() method (takes the same parameters).
8638          * @param {String/Function} url The url for this request or a function to call to get the url
8639          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8640          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8641          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8642          * @return {Roo.Element} this
8643          */
8644         load : function(){
8645             var um = this.getUpdateManager();
8646             um.update.apply(um, arguments);
8647             return this;
8648         },
8649
8650         /**
8651         * Gets this element's UpdateManager
8652         * @return {Roo.UpdateManager} The UpdateManager
8653         */
8654         getUpdateManager : function(){
8655             if(!this.updateManager){
8656                 this.updateManager = new Roo.UpdateManager(this);
8657             }
8658             return this.updateManager;
8659         },
8660
8661         /**
8662          * Disables text selection for this element (normalized across browsers)
8663          * @return {Roo.Element} this
8664          */
8665         unselectable : function(){
8666             this.dom.unselectable = "on";
8667             this.swallowEvent("selectstart", true);
8668             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8669             this.addClass("x-unselectable");
8670             return this;
8671         },
8672
8673         /**
8674         * Calculates the x, y to center this element on the screen
8675         * @return {Array} The x, y values [x, y]
8676         */
8677         getCenterXY : function(){
8678             return this.getAlignToXY(document, 'c-c');
8679         },
8680
8681         /**
8682         * Centers the Element in either the viewport, or another Element.
8683         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8684         */
8685         center : function(centerIn){
8686             this.alignTo(centerIn || document, 'c-c');
8687             return this;
8688         },
8689
8690         /**
8691          * Tests various css rules/browsers to determine if this element uses a border box
8692          * @return {Boolean}
8693          */
8694         isBorderBox : function(){
8695             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8696         },
8697
8698         /**
8699          * Return a box {x, y, width, height} that can be used to set another elements
8700          * size/location to match this element.
8701          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8702          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8703          * @return {Object} box An object in the format {x, y, width, height}
8704          */
8705         getBox : function(contentBox, local){
8706             var xy;
8707             if(!local){
8708                 xy = this.getXY();
8709             }else{
8710                 var left = parseInt(this.getStyle("left"), 10) || 0;
8711                 var top = parseInt(this.getStyle("top"), 10) || 0;
8712                 xy = [left, top];
8713             }
8714             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8715             if(!contentBox){
8716                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8717             }else{
8718                 var l = this.getBorderWidth("l")+this.getPadding("l");
8719                 var r = this.getBorderWidth("r")+this.getPadding("r");
8720                 var t = this.getBorderWidth("t")+this.getPadding("t");
8721                 var b = this.getBorderWidth("b")+this.getPadding("b");
8722                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8723             }
8724             bx.right = bx.x + bx.width;
8725             bx.bottom = bx.y + bx.height;
8726             return bx;
8727         },
8728
8729         /**
8730          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8731          for more information about the sides.
8732          * @param {String} sides
8733          * @return {Number}
8734          */
8735         getFrameWidth : function(sides, onlyContentBox){
8736             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8737         },
8738
8739         /**
8740          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8741          * @param {Object} box The box to fill {x, y, width, height}
8742          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8743          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8744          * @return {Roo.Element} this
8745          */
8746         setBox : function(box, adjust, animate){
8747             var w = box.width, h = box.height;
8748             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8749                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8750                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8751             }
8752             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8753             return this;
8754         },
8755
8756         /**
8757          * Forces the browser to repaint this element
8758          * @return {Roo.Element} this
8759          */
8760          repaint : function(){
8761             var dom = this.dom;
8762             this.addClass("x-repaint");
8763             setTimeout(function(){
8764                 Roo.get(dom).removeClass("x-repaint");
8765             }, 1);
8766             return this;
8767         },
8768
8769         /**
8770          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8771          * then it returns the calculated width of the sides (see getPadding)
8772          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8773          * @return {Object/Number}
8774          */
8775         getMargins : function(side){
8776             if(!side){
8777                 return {
8778                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8779                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8780                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8781                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8782                 };
8783             }else{
8784                 return this.addStyles(side, El.margins);
8785              }
8786         },
8787
8788         // private
8789         addStyles : function(sides, styles){
8790             var val = 0, v, w;
8791             for(var i = 0, len = sides.length; i < len; i++){
8792                 v = this.getStyle(styles[sides.charAt(i)]);
8793                 if(v){
8794                      w = parseInt(v, 10);
8795                      if(w){ val += w; }
8796                 }
8797             }
8798             return val;
8799         },
8800
8801         /**
8802          * Creates a proxy element of this element
8803          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8804          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8805          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8806          * @return {Roo.Element} The new proxy element
8807          */
8808         createProxy : function(config, renderTo, matchBox){
8809             if(renderTo){
8810                 renderTo = Roo.getDom(renderTo);
8811             }else{
8812                 renderTo = document.body;
8813             }
8814             config = typeof config == "object" ?
8815                 config : {tag : "div", cls: config};
8816             var proxy = Roo.DomHelper.append(renderTo, config, true);
8817             if(matchBox){
8818                proxy.setBox(this.getBox());
8819             }
8820             return proxy;
8821         },
8822
8823         /**
8824          * Puts a mask over this element to disable user interaction. Requires core.css.
8825          * This method can only be applied to elements which accept child nodes.
8826          * @param {String} msg (optional) A message to display in the mask
8827          * @param {String} msgCls (optional) A css class to apply to the msg element
8828          * @return {Element} The mask  element
8829          */
8830         mask : function(msg, msgCls){
8831             if(this.getStyle("position") == "static"){
8832                 this.setStyle("position", "relative");
8833             }
8834             if(!this._mask){
8835                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8836             }
8837             this.addClass("x-masked");
8838             this._mask.setDisplayed(true);
8839             if(typeof msg == 'string'){
8840                 if(!this._maskMsg){
8841                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8842                 }
8843                 var mm = this._maskMsg;
8844                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8845                 mm.dom.firstChild.innerHTML = msg;
8846                 mm.setDisplayed(true);
8847                 mm.center(this);
8848             }
8849             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8850                 this._mask.setHeight(this.getHeight());
8851             }
8852             return this._mask;
8853         },
8854
8855         /**
8856          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8857          * it is cached for reuse.
8858          */
8859         unmask : function(removeEl){
8860             if(this._mask){
8861                 if(removeEl === true){
8862                     this._mask.remove();
8863                     delete this._mask;
8864                     if(this._maskMsg){
8865                         this._maskMsg.remove();
8866                         delete this._maskMsg;
8867                     }
8868                 }else{
8869                     this._mask.setDisplayed(false);
8870                     if(this._maskMsg){
8871                         this._maskMsg.setDisplayed(false);
8872                     }
8873                 }
8874             }
8875             this.removeClass("x-masked");
8876         },
8877
8878         /**
8879          * Returns true if this element is masked
8880          * @return {Boolean}
8881          */
8882         isMasked : function(){
8883             return this._mask && this._mask.isVisible();
8884         },
8885
8886         /**
8887          * Creates an iframe shim for this element to keep selects and other windowed objects from
8888          * showing through.
8889          * @return {Roo.Element} The new shim element
8890          */
8891         createShim : function(){
8892             var el = document.createElement('iframe');
8893             el.frameBorder = 'no';
8894             el.className = 'roo-shim';
8895             if(Roo.isIE && Roo.isSecure){
8896                 el.src = Roo.SSL_SECURE_URL;
8897             }
8898             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8899             shim.autoBoxAdjust = false;
8900             return shim;
8901         },
8902
8903         /**
8904          * Removes this element from the DOM and deletes it from the cache
8905          */
8906         remove : function(){
8907             if(this.dom.parentNode){
8908                 this.dom.parentNode.removeChild(this.dom);
8909             }
8910             delete El.cache[this.dom.id];
8911         },
8912
8913         /**
8914          * Sets up event handlers to add and remove a css class when the mouse is over this element
8915          * @param {String} className
8916          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8917          * mouseout events for children elements
8918          * @return {Roo.Element} this
8919          */
8920         addClassOnOver : function(className, preventFlicker){
8921             this.on("mouseover", function(){
8922                 Roo.fly(this, '_internal').addClass(className);
8923             }, this.dom);
8924             var removeFn = function(e){
8925                 if(preventFlicker !== true || !e.within(this, true)){
8926                     Roo.fly(this, '_internal').removeClass(className);
8927                 }
8928             };
8929             this.on("mouseout", removeFn, this.dom);
8930             return this;
8931         },
8932
8933         /**
8934          * Sets up event handlers to add and remove a css class when this element has the focus
8935          * @param {String} className
8936          * @return {Roo.Element} this
8937          */
8938         addClassOnFocus : function(className){
8939             this.on("focus", function(){
8940                 Roo.fly(this, '_internal').addClass(className);
8941             }, this.dom);
8942             this.on("blur", function(){
8943                 Roo.fly(this, '_internal').removeClass(className);
8944             }, this.dom);
8945             return this;
8946         },
8947         /**
8948          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8949          * @param {String} className
8950          * @return {Roo.Element} this
8951          */
8952         addClassOnClick : function(className){
8953             var dom = this.dom;
8954             this.on("mousedown", function(){
8955                 Roo.fly(dom, '_internal').addClass(className);
8956                 var d = Roo.get(document);
8957                 var fn = function(){
8958                     Roo.fly(dom, '_internal').removeClass(className);
8959                     d.removeListener("mouseup", fn);
8960                 };
8961                 d.on("mouseup", fn);
8962             });
8963             return this;
8964         },
8965
8966         /**
8967          * Stops the specified event from bubbling and optionally prevents the default action
8968          * @param {String} eventName
8969          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8970          * @return {Roo.Element} this
8971          */
8972         swallowEvent : function(eventName, preventDefault){
8973             var fn = function(e){
8974                 e.stopPropagation();
8975                 if(preventDefault){
8976                     e.preventDefault();
8977                 }
8978             };
8979             if(eventName instanceof Array){
8980                 for(var i = 0, len = eventName.length; i < len; i++){
8981                      this.on(eventName[i], fn);
8982                 }
8983                 return this;
8984             }
8985             this.on(eventName, fn);
8986             return this;
8987         },
8988
8989         /**
8990          * @private
8991          */
8992       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8993
8994         /**
8995          * Sizes this element to its parent element's dimensions performing
8996          * neccessary box adjustments.
8997          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8998          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8999          * @return {Roo.Element} this
9000          */
9001         fitToParent : function(monitorResize, targetParent) {
9002           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9003           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9004           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9005             return;
9006           }
9007           var p = Roo.get(targetParent || this.dom.parentNode);
9008           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9009           if (monitorResize === true) {
9010             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9011             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9012           }
9013           return this;
9014         },
9015
9016         /**
9017          * Gets the next sibling, skipping text nodes
9018          * @return {HTMLElement} The next sibling or null
9019          */
9020         getNextSibling : function(){
9021             var n = this.dom.nextSibling;
9022             while(n && n.nodeType != 1){
9023                 n = n.nextSibling;
9024             }
9025             return n;
9026         },
9027
9028         /**
9029          * Gets the previous sibling, skipping text nodes
9030          * @return {HTMLElement} The previous sibling or null
9031          */
9032         getPrevSibling : function(){
9033             var n = this.dom.previousSibling;
9034             while(n && n.nodeType != 1){
9035                 n = n.previousSibling;
9036             }
9037             return n;
9038         },
9039
9040
9041         /**
9042          * Appends the passed element(s) to this element
9043          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9044          * @return {Roo.Element} this
9045          */
9046         appendChild: function(el){
9047             el = Roo.get(el);
9048             el.appendTo(this);
9049             return this;
9050         },
9051
9052         /**
9053          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9054          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9055          * automatically generated with the specified attributes.
9056          * @param {HTMLElement} insertBefore (optional) a child element of this element
9057          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9058          * @return {Roo.Element} The new child element
9059          */
9060         createChild: function(config, insertBefore, returnDom){
9061             config = config || {tag:'div'};
9062             if(insertBefore){
9063                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9064             }
9065             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9066         },
9067
9068         /**
9069          * Appends this element to the passed element
9070          * @param {String/HTMLElement/Element} el The new parent element
9071          * @return {Roo.Element} this
9072          */
9073         appendTo: function(el){
9074             el = Roo.getDom(el);
9075             el.appendChild(this.dom);
9076             return this;
9077         },
9078
9079         /**
9080          * Inserts this element before the passed element in the DOM
9081          * @param {String/HTMLElement/Element} el The element to insert before
9082          * @return {Roo.Element} this
9083          */
9084         insertBefore: function(el){
9085             el = Roo.getDom(el);
9086             el.parentNode.insertBefore(this.dom, el);
9087             return this;
9088         },
9089
9090         /**
9091          * Inserts this element after the passed element in the DOM
9092          * @param {String/HTMLElement/Element} el The element to insert after
9093          * @return {Roo.Element} this
9094          */
9095         insertAfter: function(el){
9096             el = Roo.getDom(el);
9097             el.parentNode.insertBefore(this.dom, el.nextSibling);
9098             return this;
9099         },
9100
9101         /**
9102          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9103          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9104          * @return {Roo.Element} The new child
9105          */
9106         insertFirst: function(el, returnDom){
9107             el = el || {};
9108             if(typeof el == 'object' && !el.nodeType){ // dh config
9109                 return this.createChild(el, this.dom.firstChild, returnDom);
9110             }else{
9111                 el = Roo.getDom(el);
9112                 this.dom.insertBefore(el, this.dom.firstChild);
9113                 return !returnDom ? Roo.get(el) : el;
9114             }
9115         },
9116
9117         /**
9118          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9119          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9120          * @param {String} where (optional) 'before' or 'after' defaults to before
9121          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9122          * @return {Roo.Element} the inserted Element
9123          */
9124         insertSibling: function(el, where, returnDom){
9125             where = where ? where.toLowerCase() : 'before';
9126             el = el || {};
9127             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9128
9129             if(typeof el == 'object' && !el.nodeType){ // dh config
9130                 if(where == 'after' && !this.dom.nextSibling){
9131                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9132                 }else{
9133                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9134                 }
9135
9136             }else{
9137                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9138                             where == 'before' ? this.dom : this.dom.nextSibling);
9139                 if(!returnDom){
9140                     rt = Roo.get(rt);
9141                 }
9142             }
9143             return rt;
9144         },
9145
9146         /**
9147          * Creates and wraps this element with another element
9148          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9149          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9150          * @return {HTMLElement/Element} The newly created wrapper element
9151          */
9152         wrap: function(config, returnDom){
9153             if(!config){
9154                 config = {tag: "div"};
9155             }
9156             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9157             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9158             return newEl;
9159         },
9160
9161         /**
9162          * Replaces the passed element with this element
9163          * @param {String/HTMLElement/Element} el The element to replace
9164          * @return {Roo.Element} this
9165          */
9166         replace: function(el){
9167             el = Roo.get(el);
9168             this.insertBefore(el);
9169             el.remove();
9170             return this;
9171         },
9172
9173         /**
9174          * Inserts an html fragment into this element
9175          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9176          * @param {String} html The HTML fragment
9177          * @param {Boolean} returnEl True to return an Roo.Element
9178          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9179          */
9180         insertHtml : function(where, html, returnEl){
9181             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9182             return returnEl ? Roo.get(el) : el;
9183         },
9184
9185         /**
9186          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9187          * @param {Object} o The object with the attributes
9188          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9189          * @return {Roo.Element} this
9190          */
9191         set : function(o, useSet){
9192             var el = this.dom;
9193             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9194             for(var attr in o){
9195                 if(attr == "style" || typeof o[attr] == "function") continue;
9196                 if(attr=="cls"){
9197                     el.className = o["cls"];
9198                 }else{
9199                     if(useSet) el.setAttribute(attr, o[attr]);
9200                     else el[attr] = o[attr];
9201                 }
9202             }
9203             if(o.style){
9204                 Roo.DomHelper.applyStyles(el, o.style);
9205             }
9206             return this;
9207         },
9208
9209         /**
9210          * Convenience method for constructing a KeyMap
9211          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9212          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9213          * @param {Function} fn The function to call
9214          * @param {Object} scope (optional) The scope of the function
9215          * @return {Roo.KeyMap} The KeyMap created
9216          */
9217         addKeyListener : function(key, fn, scope){
9218             var config;
9219             if(typeof key != "object" || key instanceof Array){
9220                 config = {
9221                     key: key,
9222                     fn: fn,
9223                     scope: scope
9224                 };
9225             }else{
9226                 config = {
9227                     key : key.key,
9228                     shift : key.shift,
9229                     ctrl : key.ctrl,
9230                     alt : key.alt,
9231                     fn: fn,
9232                     scope: scope
9233                 };
9234             }
9235             return new Roo.KeyMap(this, config);
9236         },
9237
9238         /**
9239          * Creates a KeyMap for this element
9240          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9241          * @return {Roo.KeyMap} The KeyMap created
9242          */
9243         addKeyMap : function(config){
9244             return new Roo.KeyMap(this, config);
9245         },
9246
9247         /**
9248          * Returns true if this element is scrollable.
9249          * @return {Boolean}
9250          */
9251          isScrollable : function(){
9252             var dom = this.dom;
9253             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9254         },
9255
9256         /**
9257          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9258          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9259          * @param {Number} value The new scroll value
9260          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9261          * @return {Element} this
9262          */
9263
9264         scrollTo : function(side, value, animate){
9265             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9266             if(!animate || !A){
9267                 this.dom[prop] = value;
9268             }else{
9269                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9270                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9271             }
9272             return this;
9273         },
9274
9275         /**
9276          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9277          * within this element's scrollable range.
9278          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9279          * @param {Number} distance How far to scroll the element in pixels
9280          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9281          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9282          * was scrolled as far as it could go.
9283          */
9284          scroll : function(direction, distance, animate){
9285              if(!this.isScrollable()){
9286                  return;
9287              }
9288              var el = this.dom;
9289              var l = el.scrollLeft, t = el.scrollTop;
9290              var w = el.scrollWidth, h = el.scrollHeight;
9291              var cw = el.clientWidth, ch = el.clientHeight;
9292              direction = direction.toLowerCase();
9293              var scrolled = false;
9294              var a = this.preanim(arguments, 2);
9295              switch(direction){
9296                  case "l":
9297                  case "left":
9298                      if(w - l > cw){
9299                          var v = Math.min(l + distance, w-cw);
9300                          this.scrollTo("left", v, a);
9301                          scrolled = true;
9302                      }
9303                      break;
9304                 case "r":
9305                 case "right":
9306                      if(l > 0){
9307                          var v = Math.max(l - distance, 0);
9308                          this.scrollTo("left", v, a);
9309                          scrolled = true;
9310                      }
9311                      break;
9312                 case "t":
9313                 case "top":
9314                 case "up":
9315                      if(t > 0){
9316                          var v = Math.max(t - distance, 0);
9317                          this.scrollTo("top", v, a);
9318                          scrolled = true;
9319                      }
9320                      break;
9321                 case "b":
9322                 case "bottom":
9323                 case "down":
9324                      if(h - t > ch){
9325                          var v = Math.min(t + distance, h-ch);
9326                          this.scrollTo("top", v, a);
9327                          scrolled = true;
9328                      }
9329                      break;
9330              }
9331              return scrolled;
9332         },
9333
9334         /**
9335          * Translates the passed page coordinates into left/top css values for this element
9336          * @param {Number/Array} x The page x or an array containing [x, y]
9337          * @param {Number} y The page y
9338          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9339          */
9340         translatePoints : function(x, y){
9341             if(typeof x == 'object' || x instanceof Array){
9342                 y = x[1]; x = x[0];
9343             }
9344             var p = this.getStyle('position');
9345             var o = this.getXY();
9346
9347             var l = parseInt(this.getStyle('left'), 10);
9348             var t = parseInt(this.getStyle('top'), 10);
9349
9350             if(isNaN(l)){
9351                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9352             }
9353             if(isNaN(t)){
9354                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9355             }
9356
9357             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9358         },
9359
9360         /**
9361          * Returns the current scroll position of the element.
9362          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9363          */
9364         getScroll : function(){
9365             var d = this.dom, doc = document;
9366             if(d == doc || d == doc.body){
9367                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9368                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9369                 return {left: l, top: t};
9370             }else{
9371                 return {left: d.scrollLeft, top: d.scrollTop};
9372             }
9373         },
9374
9375         /**
9376          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9377          * are convert to standard 6 digit hex color.
9378          * @param {String} attr The css attribute
9379          * @param {String} defaultValue The default value to use when a valid color isn't found
9380          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9381          * YUI color anims.
9382          */
9383         getColor : function(attr, defaultValue, prefix){
9384             var v = this.getStyle(attr);
9385             if(!v || v == "transparent" || v == "inherit") {
9386                 return defaultValue;
9387             }
9388             var color = typeof prefix == "undefined" ? "#" : prefix;
9389             if(v.substr(0, 4) == "rgb("){
9390                 var rvs = v.slice(4, v.length -1).split(",");
9391                 for(var i = 0; i < 3; i++){
9392                     var h = parseInt(rvs[i]).toString(16);
9393                     if(h < 16){
9394                         h = "0" + h;
9395                     }
9396                     color += h;
9397                 }
9398             } else {
9399                 if(v.substr(0, 1) == "#"){
9400                     if(v.length == 4) {
9401                         for(var i = 1; i < 4; i++){
9402                             var c = v.charAt(i);
9403                             color +=  c + c;
9404                         }
9405                     }else if(v.length == 7){
9406                         color += v.substr(1);
9407                     }
9408                 }
9409             }
9410             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9411         },
9412
9413         /**
9414          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9415          * gradient background, rounded corners and a 4-way shadow.
9416          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9417          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9418          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9419          * @return {Roo.Element} this
9420          */
9421         boxWrap : function(cls){
9422             cls = cls || 'x-box';
9423             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9424             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9425             return el;
9426         },
9427
9428         /**
9429          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9430          * @param {String} namespace The namespace in which to look for the attribute
9431          * @param {String} name The attribute name
9432          * @return {String} The attribute value
9433          */
9434         getAttributeNS : Roo.isIE ? function(ns, name){
9435             var d = this.dom;
9436             var type = typeof d[ns+":"+name];
9437             if(type != 'undefined' && type != 'unknown'){
9438                 return d[ns+":"+name];
9439             }
9440             return d[name];
9441         } : function(ns, name){
9442             var d = this.dom;
9443             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9444         }
9445     };
9446
9447     var ep = El.prototype;
9448
9449     /**
9450      * Appends an event handler (Shorthand for addListener)
9451      * @param {String}   eventName     The type of event to append
9452      * @param {Function} fn        The method the event invokes
9453      * @param {Object} scope       (optional) The scope (this object) of the fn
9454      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9455      * @method
9456      */
9457     ep.on = ep.addListener;
9458         // backwards compat
9459     ep.mon = ep.addListener;
9460
9461     /**
9462      * Removes an event handler from this element (shorthand for removeListener)
9463      * @param {String} eventName the type of event to remove
9464      * @param {Function} fn the method the event invokes
9465      * @return {Roo.Element} this
9466      * @method
9467      */
9468     ep.un = ep.removeListener;
9469
9470     /**
9471      * true to automatically adjust width and height settings for box-model issues (default to true)
9472      */
9473     ep.autoBoxAdjust = true;
9474
9475     // private
9476     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9477
9478     // private
9479     El.addUnits = function(v, defaultUnit){
9480         if(v === "" || v == "auto"){
9481             return v;
9482         }
9483         if(v === undefined){
9484             return '';
9485         }
9486         if(typeof v == "number" || !El.unitPattern.test(v)){
9487             return v + (defaultUnit || 'px');
9488         }
9489         return v;
9490     };
9491
9492     // special markup used throughout Roo when box wrapping elements
9493     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9494     /**
9495      * Visibility mode constant - Use visibility to hide element
9496      * @static
9497      * @type Number
9498      */
9499     El.VISIBILITY = 1;
9500     /**
9501      * Visibility mode constant - Use display to hide element
9502      * @static
9503      * @type Number
9504      */
9505     El.DISPLAY = 2;
9506
9507     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9508     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9509     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9510
9511
9512
9513     /**
9514      * @private
9515      */
9516     El.cache = {};
9517
9518     var docEl;
9519
9520     /**
9521      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9522      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9523      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9524      * @return {Element} The Element object
9525      * @static
9526      */
9527     El.get = function(el){
9528         var ex, elm, id;
9529         if(!el){ return null; }
9530         if(typeof el == "string"){ // element id
9531             if(!(elm = document.getElementById(el))){
9532                 return null;
9533             }
9534             if(ex = El.cache[el]){
9535                 ex.dom = elm;
9536             }else{
9537                 ex = El.cache[el] = new El(elm);
9538             }
9539             return ex;
9540         }else if(el.tagName){ // dom element
9541             if(!(id = el.id)){
9542                 id = Roo.id(el);
9543             }
9544             if(ex = El.cache[id]){
9545                 ex.dom = el;
9546             }else{
9547                 ex = El.cache[id] = new El(el);
9548             }
9549             return ex;
9550         }else if(el instanceof El){
9551             if(el != docEl){
9552                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9553                                                               // catch case where it hasn't been appended
9554                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9555             }
9556             return el;
9557         }else if(el.isComposite){
9558             return el;
9559         }else if(el instanceof Array){
9560             return El.select(el);
9561         }else if(el == document){
9562             // create a bogus element object representing the document object
9563             if(!docEl){
9564                 var f = function(){};
9565                 f.prototype = El.prototype;
9566                 docEl = new f();
9567                 docEl.dom = document;
9568             }
9569             return docEl;
9570         }
9571         return null;
9572     };
9573
9574     // private
9575     El.uncache = function(el){
9576         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9577             if(a[i]){
9578                 delete El.cache[a[i].id || a[i]];
9579             }
9580         }
9581     };
9582
9583     // private
9584     // Garbage collection - uncache elements/purge listeners on orphaned elements
9585     // so we don't hold a reference and cause the browser to retain them
9586     El.garbageCollect = function(){
9587         if(!Roo.enableGarbageCollector){
9588             clearInterval(El.collectorThread);
9589             return;
9590         }
9591         for(var eid in El.cache){
9592             var el = El.cache[eid], d = el.dom;
9593             // -------------------------------------------------------
9594             // Determining what is garbage:
9595             // -------------------------------------------------------
9596             // !d
9597             // dom node is null, definitely garbage
9598             // -------------------------------------------------------
9599             // !d.parentNode
9600             // no parentNode == direct orphan, definitely garbage
9601             // -------------------------------------------------------
9602             // !d.offsetParent && !document.getElementById(eid)
9603             // display none elements have no offsetParent so we will
9604             // also try to look it up by it's id. However, check
9605             // offsetParent first so we don't do unneeded lookups.
9606             // This enables collection of elements that are not orphans
9607             // directly, but somewhere up the line they have an orphan
9608             // parent.
9609             // -------------------------------------------------------
9610             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9611                 delete El.cache[eid];
9612                 if(d && Roo.enableListenerCollection){
9613                     E.purgeElement(d);
9614                 }
9615             }
9616         }
9617     }
9618     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9619
9620
9621     // dom is optional
9622     El.Flyweight = function(dom){
9623         this.dom = dom;
9624     };
9625     El.Flyweight.prototype = El.prototype;
9626
9627     El._flyweights = {};
9628     /**
9629      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9630      * the dom node can be overwritten by other code.
9631      * @param {String/HTMLElement} el The dom node or id
9632      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9633      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9634      * @static
9635      * @return {Element} The shared Element object
9636      */
9637     El.fly = function(el, named){
9638         named = named || '_global';
9639         el = Roo.getDom(el);
9640         if(!el){
9641             return null;
9642         }
9643         if(!El._flyweights[named]){
9644             El._flyweights[named] = new El.Flyweight();
9645         }
9646         El._flyweights[named].dom = el;
9647         return El._flyweights[named];
9648     };
9649
9650     /**
9651      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9652      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9653      * Shorthand of {@link Roo.Element#get}
9654      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9655      * @return {Element} The Element object
9656      * @member Roo
9657      * @method get
9658      */
9659     Roo.get = El.get;
9660     /**
9661      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9662      * the dom node can be overwritten by other code.
9663      * Shorthand of {@link Roo.Element#fly}
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      * @member Roo
9670      * @method fly
9671      */
9672     Roo.fly = El.fly;
9673
9674     // speedy lookup for elements never to box adjust
9675     var noBoxAdjust = Roo.isStrict ? {
9676         select:1
9677     } : {
9678         input:1, select:1, textarea:1
9679     };
9680     if(Roo.isIE || Roo.isGecko){
9681         noBoxAdjust['button'] = 1;
9682     }
9683
9684
9685     Roo.EventManager.on(window, 'unload', function(){
9686         delete El.cache;
9687         delete El._flyweights;
9688     });
9689 })();
9690
9691
9692
9693
9694 if(Roo.DomQuery){
9695     Roo.Element.selectorFunction = Roo.DomQuery.select;
9696 }
9697
9698 Roo.Element.select = function(selector, unique, root){
9699     var els;
9700     if(typeof selector == "string"){
9701         els = Roo.Element.selectorFunction(selector, root);
9702     }else if(selector.length !== undefined){
9703         els = selector;
9704     }else{
9705         throw "Invalid selector";
9706     }
9707     if(unique === true){
9708         return new Roo.CompositeElement(els);
9709     }else{
9710         return new Roo.CompositeElementLite(els);
9711     }
9712 };
9713 /**
9714  * Selects elements based on the passed CSS selector to enable working on them as 1.
9715  * @param {String/Array} selector The CSS selector or an array of elements
9716  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9717  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9718  * @return {CompositeElementLite/CompositeElement}
9719  * @member Roo
9720  * @method select
9721  */
9722 Roo.select = Roo.Element.select;
9723
9724
9725
9726
9727
9728
9729
9730
9731
9732
9733
9734
9735
9736
9737 /*
9738  * Based on:
9739  * Ext JS Library 1.1.1
9740  * Copyright(c) 2006-2007, Ext JS, LLC.
9741  *
9742  * Originally Released Under LGPL - original licence link has changed is not relivant.
9743  *
9744  * Fork - LGPL
9745  * <script type="text/javascript">
9746  */
9747
9748
9749
9750 //Notifies Element that fx methods are available
9751 Roo.enableFx = true;
9752
9753 /**
9754  * @class Roo.Fx
9755  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9756  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9757  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9758  * Element effects to work.</p><br/>
9759  *
9760  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9761  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9762  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9763  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9764  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9765  * expected results and should be done with care.</p><br/>
9766  *
9767  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9768  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9769 <pre>
9770 Value  Description
9771 -----  -----------------------------
9772 tl     The top left corner
9773 t      The center of the top edge
9774 tr     The top right corner
9775 l      The center of the left edge
9776 r      The center of the right edge
9777 bl     The bottom left corner
9778 b      The center of the bottom edge
9779 br     The bottom right corner
9780 </pre>
9781  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9782  * below are common options that can be passed to any Fx method.</b>
9783  * @cfg {Function} callback A function called when the effect is finished
9784  * @cfg {Object} scope The scope of the effect function
9785  * @cfg {String} easing A valid Easing value for the effect
9786  * @cfg {String} afterCls A css class to apply after the effect
9787  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9788  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9789  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9790  * effects that end with the element being visually hidden, ignored otherwise)
9791  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9792  * a function which returns such a specification that will be applied to the Element after the effect finishes
9793  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9794  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9795  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9796  */
9797 Roo.Fx = {
9798         /**
9799          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9800          * origin for the slide effect.  This function automatically handles wrapping the element with
9801          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9802          * Usage:
9803          *<pre><code>
9804 // default: slide the element in from the top
9805 el.slideIn();
9806
9807 // custom: slide the element in from the right with a 2-second duration
9808 el.slideIn('r', { duration: 2 });
9809
9810 // common config options shown with default values
9811 el.slideIn('t', {
9812     easing: 'easeOut',
9813     duration: .5
9814 });
9815 </code></pre>
9816          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9817          * @param {Object} options (optional) Object literal with any of the Fx config options
9818          * @return {Roo.Element} The Element
9819          */
9820     slideIn : function(anchor, o){
9821         var el = this.getFxEl();
9822         o = o || {};
9823
9824         el.queueFx(o, function(){
9825
9826             anchor = anchor || "t";
9827
9828             // fix display to visibility
9829             this.fixDisplay();
9830
9831             // restore values after effect
9832             var r = this.getFxRestore();
9833             var b = this.getBox();
9834             // fixed size for slide
9835             this.setSize(b);
9836
9837             // wrap if needed
9838             var wrap = this.fxWrap(r.pos, o, "hidden");
9839
9840             var st = this.dom.style;
9841             st.visibility = "visible";
9842             st.position = "absolute";
9843
9844             // clear out temp styles after slide and unwrap
9845             var after = function(){
9846                 el.fxUnwrap(wrap, r.pos, o);
9847                 st.width = r.width;
9848                 st.height = r.height;
9849                 el.afterFx(o);
9850             };
9851             // time to calc the positions
9852             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9853
9854             switch(anchor.toLowerCase()){
9855                 case "t":
9856                     wrap.setSize(b.width, 0);
9857                     st.left = st.bottom = "0";
9858                     a = {height: bh};
9859                 break;
9860                 case "l":
9861                     wrap.setSize(0, b.height);
9862                     st.right = st.top = "0";
9863                     a = {width: bw};
9864                 break;
9865                 case "r":
9866                     wrap.setSize(0, b.height);
9867                     wrap.setX(b.right);
9868                     st.left = st.top = "0";
9869                     a = {width: bw, points: pt};
9870                 break;
9871                 case "b":
9872                     wrap.setSize(b.width, 0);
9873                     wrap.setY(b.bottom);
9874                     st.left = st.top = "0";
9875                     a = {height: bh, points: pt};
9876                 break;
9877                 case "tl":
9878                     wrap.setSize(0, 0);
9879                     st.right = st.bottom = "0";
9880                     a = {width: bw, height: bh};
9881                 break;
9882                 case "bl":
9883                     wrap.setSize(0, 0);
9884                     wrap.setY(b.y+b.height);
9885                     st.right = st.top = "0";
9886                     a = {width: bw, height: bh, points: pt};
9887                 break;
9888                 case "br":
9889                     wrap.setSize(0, 0);
9890                     wrap.setXY([b.right, b.bottom]);
9891                     st.left = st.top = "0";
9892                     a = {width: bw, height: bh, points: pt};
9893                 break;
9894                 case "tr":
9895                     wrap.setSize(0, 0);
9896                     wrap.setX(b.x+b.width);
9897                     st.left = st.bottom = "0";
9898                     a = {width: bw, height: bh, points: pt};
9899                 break;
9900             }
9901             this.dom.style.visibility = "visible";
9902             wrap.show();
9903
9904             arguments.callee.anim = wrap.fxanim(a,
9905                 o,
9906                 'motion',
9907                 .5,
9908                 'easeOut', after);
9909         });
9910         return this;
9911     },
9912     
9913         /**
9914          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9915          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9916          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9917          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9918          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9919          * Usage:
9920          *<pre><code>
9921 // default: slide the element out to the top
9922 el.slideOut();
9923
9924 // custom: slide the element out to the right with a 2-second duration
9925 el.slideOut('r', { duration: 2 });
9926
9927 // common config options shown with default values
9928 el.slideOut('t', {
9929     easing: 'easeOut',
9930     duration: .5,
9931     remove: false,
9932     useDisplay: false
9933 });
9934 </code></pre>
9935          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9936          * @param {Object} options (optional) Object literal with any of the Fx config options
9937          * @return {Roo.Element} The Element
9938          */
9939     slideOut : function(anchor, o){
9940         var el = this.getFxEl();
9941         o = o || {};
9942
9943         el.queueFx(o, function(){
9944
9945             anchor = anchor || "t";
9946
9947             // restore values after effect
9948             var r = this.getFxRestore();
9949             
9950             var b = this.getBox();
9951             // fixed size for slide
9952             this.setSize(b);
9953
9954             // wrap if needed
9955             var wrap = this.fxWrap(r.pos, o, "visible");
9956
9957             var st = this.dom.style;
9958             st.visibility = "visible";
9959             st.position = "absolute";
9960
9961             wrap.setSize(b);
9962
9963             var after = function(){
9964                 if(o.useDisplay){
9965                     el.setDisplayed(false);
9966                 }else{
9967                     el.hide();
9968                 }
9969
9970                 el.fxUnwrap(wrap, r.pos, o);
9971
9972                 st.width = r.width;
9973                 st.height = r.height;
9974
9975                 el.afterFx(o);
9976             };
9977
9978             var a, zero = {to: 0};
9979             switch(anchor.toLowerCase()){
9980                 case "t":
9981                     st.left = st.bottom = "0";
9982                     a = {height: zero};
9983                 break;
9984                 case "l":
9985                     st.right = st.top = "0";
9986                     a = {width: zero};
9987                 break;
9988                 case "r":
9989                     st.left = st.top = "0";
9990                     a = {width: zero, points: {to:[b.right, b.y]}};
9991                 break;
9992                 case "b":
9993                     st.left = st.top = "0";
9994                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9995                 break;
9996                 case "tl":
9997                     st.right = st.bottom = "0";
9998                     a = {width: zero, height: zero};
9999                 break;
10000                 case "bl":
10001                     st.right = st.top = "0";
10002                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10003                 break;
10004                 case "br":
10005                     st.left = st.top = "0";
10006                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10007                 break;
10008                 case "tr":
10009                     st.left = st.bottom = "0";
10010                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10011                 break;
10012             }
10013
10014             arguments.callee.anim = wrap.fxanim(a,
10015                 o,
10016                 'motion',
10017                 .5,
10018                 "easeOut", after);
10019         });
10020         return this;
10021     },
10022
10023         /**
10024          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10025          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10026          * The element must be removed from the DOM using the 'remove' config option if desired.
10027          * Usage:
10028          *<pre><code>
10029 // default
10030 el.puff();
10031
10032 // common config options shown with default values
10033 el.puff({
10034     easing: 'easeOut',
10035     duration: .5,
10036     remove: false,
10037     useDisplay: false
10038 });
10039 </code></pre>
10040          * @param {Object} options (optional) Object literal with any of the Fx config options
10041          * @return {Roo.Element} The Element
10042          */
10043     puff : function(o){
10044         var el = this.getFxEl();
10045         o = o || {};
10046
10047         el.queueFx(o, function(){
10048             this.clearOpacity();
10049             this.show();
10050
10051             // restore values after effect
10052             var r = this.getFxRestore();
10053             var st = this.dom.style;
10054
10055             var after = function(){
10056                 if(o.useDisplay){
10057                     el.setDisplayed(false);
10058                 }else{
10059                     el.hide();
10060                 }
10061
10062                 el.clearOpacity();
10063
10064                 el.setPositioning(r.pos);
10065                 st.width = r.width;
10066                 st.height = r.height;
10067                 st.fontSize = '';
10068                 el.afterFx(o);
10069             };
10070
10071             var width = this.getWidth();
10072             var height = this.getHeight();
10073
10074             arguments.callee.anim = this.fxanim({
10075                     width : {to: this.adjustWidth(width * 2)},
10076                     height : {to: this.adjustHeight(height * 2)},
10077                     points : {by: [-(width * .5), -(height * .5)]},
10078                     opacity : {to: 0},
10079                     fontSize: {to:200, unit: "%"}
10080                 },
10081                 o,
10082                 'motion',
10083                 .5,
10084                 "easeOut", after);
10085         });
10086         return this;
10087     },
10088
10089         /**
10090          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10091          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10092          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10093          * Usage:
10094          *<pre><code>
10095 // default
10096 el.switchOff();
10097
10098 // all config options shown with default values
10099 el.switchOff({
10100     easing: 'easeIn',
10101     duration: .3,
10102     remove: false,
10103     useDisplay: false
10104 });
10105 </code></pre>
10106          * @param {Object} options (optional) Object literal with any of the Fx config options
10107          * @return {Roo.Element} The Element
10108          */
10109     switchOff : function(o){
10110         var el = this.getFxEl();
10111         o = o || {};
10112
10113         el.queueFx(o, function(){
10114             this.clearOpacity();
10115             this.clip();
10116
10117             // restore values after effect
10118             var r = this.getFxRestore();
10119             var st = this.dom.style;
10120
10121             var after = function(){
10122                 if(o.useDisplay){
10123                     el.setDisplayed(false);
10124                 }else{
10125                     el.hide();
10126                 }
10127
10128                 el.clearOpacity();
10129                 el.setPositioning(r.pos);
10130                 st.width = r.width;
10131                 st.height = r.height;
10132
10133                 el.afterFx(o);
10134             };
10135
10136             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10137                 this.clearOpacity();
10138                 (function(){
10139                     this.fxanim({
10140                         height:{to:1},
10141                         points:{by:[0, this.getHeight() * .5]}
10142                     }, o, 'motion', 0.3, 'easeIn', after);
10143                 }).defer(100, this);
10144             });
10145         });
10146         return this;
10147     },
10148
10149     /**
10150      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10151      * changed using the "attr" config option) and then fading back to the original color. If no original
10152      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10153      * Usage:
10154 <pre><code>
10155 // default: highlight background to yellow
10156 el.highlight();
10157
10158 // custom: highlight foreground text to blue for 2 seconds
10159 el.highlight("0000ff", { attr: 'color', duration: 2 });
10160
10161 // common config options shown with default values
10162 el.highlight("ffff9c", {
10163     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10164     endColor: (current color) or "ffffff",
10165     easing: 'easeIn',
10166     duration: 1
10167 });
10168 </code></pre>
10169      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10170      * @param {Object} options (optional) Object literal with any of the Fx config options
10171      * @return {Roo.Element} The Element
10172      */ 
10173     highlight : function(color, o){
10174         var el = this.getFxEl();
10175         o = o || {};
10176
10177         el.queueFx(o, function(){
10178             color = color || "ffff9c";
10179             attr = o.attr || "backgroundColor";
10180
10181             this.clearOpacity();
10182             this.show();
10183
10184             var origColor = this.getColor(attr);
10185             var restoreColor = this.dom.style[attr];
10186             endColor = (o.endColor || origColor) || "ffffff";
10187
10188             var after = function(){
10189                 el.dom.style[attr] = restoreColor;
10190                 el.afterFx(o);
10191             };
10192
10193             var a = {};
10194             a[attr] = {from: color, to: endColor};
10195             arguments.callee.anim = this.fxanim(a,
10196                 o,
10197                 'color',
10198                 1,
10199                 'easeIn', after);
10200         });
10201         return this;
10202     },
10203
10204    /**
10205     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10206     * Usage:
10207 <pre><code>
10208 // default: a single light blue ripple
10209 el.frame();
10210
10211 // custom: 3 red ripples lasting 3 seconds total
10212 el.frame("ff0000", 3, { duration: 3 });
10213
10214 // common config options shown with default values
10215 el.frame("C3DAF9", 1, {
10216     duration: 1 //duration of entire animation (not each individual ripple)
10217     // Note: Easing is not configurable and will be ignored if included
10218 });
10219 </code></pre>
10220     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10221     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10222     * @param {Object} options (optional) Object literal with any of the Fx config options
10223     * @return {Roo.Element} The Element
10224     */
10225     frame : function(color, count, o){
10226         var el = this.getFxEl();
10227         o = o || {};
10228
10229         el.queueFx(o, function(){
10230             color = color || "#C3DAF9";
10231             if(color.length == 6){
10232                 color = "#" + color;
10233             }
10234             count = count || 1;
10235             duration = o.duration || 1;
10236             this.show();
10237
10238             var b = this.getBox();
10239             var animFn = function(){
10240                 var proxy = this.createProxy({
10241
10242                      style:{
10243                         visbility:"hidden",
10244                         position:"absolute",
10245                         "z-index":"35000", // yee haw
10246                         border:"0px solid " + color
10247                      }
10248                   });
10249                 var scale = Roo.isBorderBox ? 2 : 1;
10250                 proxy.animate({
10251                     top:{from:b.y, to:b.y - 20},
10252                     left:{from:b.x, to:b.x - 20},
10253                     borderWidth:{from:0, to:10},
10254                     opacity:{from:1, to:0},
10255                     height:{from:b.height, to:(b.height + (20*scale))},
10256                     width:{from:b.width, to:(b.width + (20*scale))}
10257                 }, duration, function(){
10258                     proxy.remove();
10259                 });
10260                 if(--count > 0){
10261                      animFn.defer((duration/2)*1000, this);
10262                 }else{
10263                     el.afterFx(o);
10264                 }
10265             };
10266             animFn.call(this);
10267         });
10268         return this;
10269     },
10270
10271    /**
10272     * Creates a pause before any subsequent queued effects begin.  If there are
10273     * no effects queued after the pause it will have no effect.
10274     * Usage:
10275 <pre><code>
10276 el.pause(1);
10277 </code></pre>
10278     * @param {Number} seconds The length of time to pause (in seconds)
10279     * @return {Roo.Element} The Element
10280     */
10281     pause : function(seconds){
10282         var el = this.getFxEl();
10283         var o = {};
10284
10285         el.queueFx(o, function(){
10286             setTimeout(function(){
10287                 el.afterFx(o);
10288             }, seconds * 1000);
10289         });
10290         return this;
10291     },
10292
10293    /**
10294     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10295     * using the "endOpacity" config option.
10296     * Usage:
10297 <pre><code>
10298 // default: fade in from opacity 0 to 100%
10299 el.fadeIn();
10300
10301 // custom: fade in from opacity 0 to 75% over 2 seconds
10302 el.fadeIn({ endOpacity: .75, duration: 2});
10303
10304 // common config options shown with default values
10305 el.fadeIn({
10306     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10307     easing: 'easeOut',
10308     duration: .5
10309 });
10310 </code></pre>
10311     * @param {Object} options (optional) Object literal with any of the Fx config options
10312     * @return {Roo.Element} The Element
10313     */
10314     fadeIn : function(o){
10315         var el = this.getFxEl();
10316         o = o || {};
10317         el.queueFx(o, function(){
10318             this.setOpacity(0);
10319             this.fixDisplay();
10320             this.dom.style.visibility = 'visible';
10321             var to = o.endOpacity || 1;
10322             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10323                 o, null, .5, "easeOut", function(){
10324                 if(to == 1){
10325                     this.clearOpacity();
10326                 }
10327                 el.afterFx(o);
10328             });
10329         });
10330         return this;
10331     },
10332
10333    /**
10334     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10335     * using the "endOpacity" config option.
10336     * Usage:
10337 <pre><code>
10338 // default: fade out from the element's current opacity to 0
10339 el.fadeOut();
10340
10341 // custom: fade out from the element's current opacity to 25% over 2 seconds
10342 el.fadeOut({ endOpacity: .25, duration: 2});
10343
10344 // common config options shown with default values
10345 el.fadeOut({
10346     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10347     easing: 'easeOut',
10348     duration: .5
10349     remove: false,
10350     useDisplay: false
10351 });
10352 </code></pre>
10353     * @param {Object} options (optional) Object literal with any of the Fx config options
10354     * @return {Roo.Element} The Element
10355     */
10356     fadeOut : function(o){
10357         var el = this.getFxEl();
10358         o = o || {};
10359         el.queueFx(o, function(){
10360             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10361                 o, null, .5, "easeOut", function(){
10362                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10363                      this.dom.style.display = "none";
10364                 }else{
10365                      this.dom.style.visibility = "hidden";
10366                 }
10367                 this.clearOpacity();
10368                 el.afterFx(o);
10369             });
10370         });
10371         return this;
10372     },
10373
10374    /**
10375     * Animates the transition of an element's dimensions from a starting height/width
10376     * to an ending height/width.
10377     * Usage:
10378 <pre><code>
10379 // change height and width to 100x100 pixels
10380 el.scale(100, 100);
10381
10382 // common config options shown with default values.  The height and width will default to
10383 // the element's existing values if passed as null.
10384 el.scale(
10385     [element's width],
10386     [element's height], {
10387     easing: 'easeOut',
10388     duration: .35
10389 });
10390 </code></pre>
10391     * @param {Number} width  The new width (pass undefined to keep the original width)
10392     * @param {Number} height  The new height (pass undefined to keep the original height)
10393     * @param {Object} options (optional) Object literal with any of the Fx config options
10394     * @return {Roo.Element} The Element
10395     */
10396     scale : function(w, h, o){
10397         this.shift(Roo.apply({}, o, {
10398             width: w,
10399             height: h
10400         }));
10401         return this;
10402     },
10403
10404    /**
10405     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10406     * Any of these properties not specified in the config object will not be changed.  This effect 
10407     * requires that at least one new dimension, position or opacity setting must be passed in on
10408     * the config object in order for the function to have any effect.
10409     * Usage:
10410 <pre><code>
10411 // slide the element horizontally to x position 200 while changing the height and opacity
10412 el.shift({ x: 200, height: 50, opacity: .8 });
10413
10414 // common config options shown with default values.
10415 el.shift({
10416     width: [element's width],
10417     height: [element's height],
10418     x: [element's x position],
10419     y: [element's y position],
10420     opacity: [element's opacity],
10421     easing: 'easeOut',
10422     duration: .35
10423 });
10424 </code></pre>
10425     * @param {Object} options  Object literal with any of the Fx config options
10426     * @return {Roo.Element} The Element
10427     */
10428     shift : function(o){
10429         var el = this.getFxEl();
10430         o = o || {};
10431         el.queueFx(o, function(){
10432             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10433             if(w !== undefined){
10434                 a.width = {to: this.adjustWidth(w)};
10435             }
10436             if(h !== undefined){
10437                 a.height = {to: this.adjustHeight(h)};
10438             }
10439             if(x !== undefined || y !== undefined){
10440                 a.points = {to: [
10441                     x !== undefined ? x : this.getX(),
10442                     y !== undefined ? y : this.getY()
10443                 ]};
10444             }
10445             if(op !== undefined){
10446                 a.opacity = {to: op};
10447             }
10448             if(o.xy !== undefined){
10449                 a.points = {to: o.xy};
10450             }
10451             arguments.callee.anim = this.fxanim(a,
10452                 o, 'motion', .35, "easeOut", function(){
10453                 el.afterFx(o);
10454             });
10455         });
10456         return this;
10457     },
10458
10459         /**
10460          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10461          * ending point of the effect.
10462          * Usage:
10463          *<pre><code>
10464 // default: slide the element downward while fading out
10465 el.ghost();
10466
10467 // custom: slide the element out to the right with a 2-second duration
10468 el.ghost('r', { duration: 2 });
10469
10470 // common config options shown with default values
10471 el.ghost('b', {
10472     easing: 'easeOut',
10473     duration: .5
10474     remove: false,
10475     useDisplay: false
10476 });
10477 </code></pre>
10478          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10479          * @param {Object} options (optional) Object literal with any of the Fx config options
10480          * @return {Roo.Element} The Element
10481          */
10482     ghost : function(anchor, o){
10483         var el = this.getFxEl();
10484         o = o || {};
10485
10486         el.queueFx(o, function(){
10487             anchor = anchor || "b";
10488
10489             // restore values after effect
10490             var r = this.getFxRestore();
10491             var w = this.getWidth(),
10492                 h = this.getHeight();
10493
10494             var st = this.dom.style;
10495
10496             var after = function(){
10497                 if(o.useDisplay){
10498                     el.setDisplayed(false);
10499                 }else{
10500                     el.hide();
10501                 }
10502
10503                 el.clearOpacity();
10504                 el.setPositioning(r.pos);
10505                 st.width = r.width;
10506                 st.height = r.height;
10507
10508                 el.afterFx(o);
10509             };
10510
10511             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10512             switch(anchor.toLowerCase()){
10513                 case "t":
10514                     pt.by = [0, -h];
10515                 break;
10516                 case "l":
10517                     pt.by = [-w, 0];
10518                 break;
10519                 case "r":
10520                     pt.by = [w, 0];
10521                 break;
10522                 case "b":
10523                     pt.by = [0, h];
10524                 break;
10525                 case "tl":
10526                     pt.by = [-w, -h];
10527                 break;
10528                 case "bl":
10529                     pt.by = [-w, h];
10530                 break;
10531                 case "br":
10532                     pt.by = [w, h];
10533                 break;
10534                 case "tr":
10535                     pt.by = [w, -h];
10536                 break;
10537             }
10538
10539             arguments.callee.anim = this.fxanim(a,
10540                 o,
10541                 'motion',
10542                 .5,
10543                 "easeOut", after);
10544         });
10545         return this;
10546     },
10547
10548         /**
10549          * Ensures that all effects queued after syncFx is called on the element are
10550          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10551          * @return {Roo.Element} The Element
10552          */
10553     syncFx : function(){
10554         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10555             block : false,
10556             concurrent : true,
10557             stopFx : false
10558         });
10559         return this;
10560     },
10561
10562         /**
10563          * Ensures that all effects queued after sequenceFx is called on the element are
10564          * run in sequence.  This is the opposite of {@link #syncFx}.
10565          * @return {Roo.Element} The Element
10566          */
10567     sequenceFx : function(){
10568         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10569             block : false,
10570             concurrent : false,
10571             stopFx : false
10572         });
10573         return this;
10574     },
10575
10576         /* @private */
10577     nextFx : function(){
10578         var ef = this.fxQueue[0];
10579         if(ef){
10580             ef.call(this);
10581         }
10582     },
10583
10584         /**
10585          * Returns true if the element has any effects actively running or queued, else returns false.
10586          * @return {Boolean} True if element has active effects, else false
10587          */
10588     hasActiveFx : function(){
10589         return this.fxQueue && this.fxQueue[0];
10590     },
10591
10592         /**
10593          * Stops any running effects and clears the element's internal effects queue if it contains
10594          * any additional effects that haven't started yet.
10595          * @return {Roo.Element} The Element
10596          */
10597     stopFx : function(){
10598         if(this.hasActiveFx()){
10599             var cur = this.fxQueue[0];
10600             if(cur && cur.anim && cur.anim.isAnimated()){
10601                 this.fxQueue = [cur]; // clear out others
10602                 cur.anim.stop(true);
10603             }
10604         }
10605         return this;
10606     },
10607
10608         /* @private */
10609     beforeFx : function(o){
10610         if(this.hasActiveFx() && !o.concurrent){
10611            if(o.stopFx){
10612                this.stopFx();
10613                return true;
10614            }
10615            return false;
10616         }
10617         return true;
10618     },
10619
10620         /**
10621          * Returns true if the element is currently blocking so that no other effect can be queued
10622          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10623          * used to ensure that an effect initiated by a user action runs to completion prior to the
10624          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10625          * @return {Boolean} True if blocking, else false
10626          */
10627     hasFxBlock : function(){
10628         var q = this.fxQueue;
10629         return q && q[0] && q[0].block;
10630     },
10631
10632         /* @private */
10633     queueFx : function(o, fn){
10634         if(!this.fxQueue){
10635             this.fxQueue = [];
10636         }
10637         if(!this.hasFxBlock()){
10638             Roo.applyIf(o, this.fxDefaults);
10639             if(!o.concurrent){
10640                 var run = this.beforeFx(o);
10641                 fn.block = o.block;
10642                 this.fxQueue.push(fn);
10643                 if(run){
10644                     this.nextFx();
10645                 }
10646             }else{
10647                 fn.call(this);
10648             }
10649         }
10650         return this;
10651     },
10652
10653         /* @private */
10654     fxWrap : function(pos, o, vis){
10655         var wrap;
10656         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10657             var wrapXY;
10658             if(o.fixPosition){
10659                 wrapXY = this.getXY();
10660             }
10661             var div = document.createElement("div");
10662             div.style.visibility = vis;
10663             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10664             wrap.setPositioning(pos);
10665             if(wrap.getStyle("position") == "static"){
10666                 wrap.position("relative");
10667             }
10668             this.clearPositioning('auto');
10669             wrap.clip();
10670             wrap.dom.appendChild(this.dom);
10671             if(wrapXY){
10672                 wrap.setXY(wrapXY);
10673             }
10674         }
10675         return wrap;
10676     },
10677
10678         /* @private */
10679     fxUnwrap : function(wrap, pos, o){
10680         this.clearPositioning();
10681         this.setPositioning(pos);
10682         if(!o.wrap){
10683             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10684             wrap.remove();
10685         }
10686     },
10687
10688         /* @private */
10689     getFxRestore : function(){
10690         var st = this.dom.style;
10691         return {pos: this.getPositioning(), width: st.width, height : st.height};
10692     },
10693
10694         /* @private */
10695     afterFx : function(o){
10696         if(o.afterStyle){
10697             this.applyStyles(o.afterStyle);
10698         }
10699         if(o.afterCls){
10700             this.addClass(o.afterCls);
10701         }
10702         if(o.remove === true){
10703             this.remove();
10704         }
10705         Roo.callback(o.callback, o.scope, [this]);
10706         if(!o.concurrent){
10707             this.fxQueue.shift();
10708             this.nextFx();
10709         }
10710     },
10711
10712         /* @private */
10713     getFxEl : function(){ // support for composite element fx
10714         return Roo.get(this.dom);
10715     },
10716
10717         /* @private */
10718     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10719         animType = animType || 'run';
10720         opt = opt || {};
10721         var anim = Roo.lib.Anim[animType](
10722             this.dom, args,
10723             (opt.duration || defaultDur) || .35,
10724             (opt.easing || defaultEase) || 'easeOut',
10725             function(){
10726                 Roo.callback(cb, this);
10727             },
10728             this
10729         );
10730         opt.anim = anim;
10731         return anim;
10732     }
10733 };
10734
10735 // backwords compat
10736 Roo.Fx.resize = Roo.Fx.scale;
10737
10738 //When included, Roo.Fx is automatically applied to Element so that all basic
10739 //effects are available directly via the Element API
10740 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10741  * Based on:
10742  * Ext JS Library 1.1.1
10743  * Copyright(c) 2006-2007, Ext JS, LLC.
10744  *
10745  * Originally Released Under LGPL - original licence link has changed is not relivant.
10746  *
10747  * Fork - LGPL
10748  * <script type="text/javascript">
10749  */
10750
10751
10752 /**
10753  * @class Roo.CompositeElement
10754  * Standard composite class. Creates a Roo.Element for every element in the collection.
10755  * <br><br>
10756  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10757  * actions will be performed on all the elements in this collection.</b>
10758  * <br><br>
10759  * All methods return <i>this</i> and can be chained.
10760  <pre><code>
10761  var els = Roo.select("#some-el div.some-class", true);
10762  // or select directly from an existing element
10763  var el = Roo.get('some-el');
10764  el.select('div.some-class', true);
10765
10766  els.setWidth(100); // all elements become 100 width
10767  els.hide(true); // all elements fade out and hide
10768  // or
10769  els.setWidth(100).hide(true);
10770  </code></pre>
10771  */
10772 Roo.CompositeElement = function(els){
10773     this.elements = [];
10774     this.addElements(els);
10775 };
10776 Roo.CompositeElement.prototype = {
10777     isComposite: true,
10778     addElements : function(els){
10779         if(!els) return this;
10780         if(typeof els == "string"){
10781             els = Roo.Element.selectorFunction(els);
10782         }
10783         var yels = this.elements;
10784         var index = yels.length-1;
10785         for(var i = 0, len = els.length; i < len; i++) {
10786                 yels[++index] = Roo.get(els[i]);
10787         }
10788         return this;
10789     },
10790
10791     /**
10792     * Clears this composite and adds the elements returned by the passed selector.
10793     * @param {String/Array} els A string CSS selector, an array of elements or an element
10794     * @return {CompositeElement} this
10795     */
10796     fill : function(els){
10797         this.elements = [];
10798         this.add(els);
10799         return this;
10800     },
10801
10802     /**
10803     * Filters this composite to only elements that match the passed selector.
10804     * @param {String} selector A string CSS selector
10805     * @return {CompositeElement} this
10806     */
10807     filter : function(selector){
10808         var els = [];
10809         this.each(function(el){
10810             if(el.is(selector)){
10811                 els[els.length] = el.dom;
10812             }
10813         });
10814         this.fill(els);
10815         return this;
10816     },
10817
10818     invoke : function(fn, args){
10819         var els = this.elements;
10820         for(var i = 0, len = els.length; i < len; i++) {
10821                 Roo.Element.prototype[fn].apply(els[i], args);
10822         }
10823         return this;
10824     },
10825     /**
10826     * Adds elements to this composite.
10827     * @param {String/Array} els A string CSS selector, an array of elements or an element
10828     * @return {CompositeElement} this
10829     */
10830     add : function(els){
10831         if(typeof els == "string"){
10832             this.addElements(Roo.Element.selectorFunction(els));
10833         }else if(els.length !== undefined){
10834             this.addElements(els);
10835         }else{
10836             this.addElements([els]);
10837         }
10838         return this;
10839     },
10840     /**
10841     * Calls the passed function passing (el, this, index) for each element in this composite.
10842     * @param {Function} fn The function to call
10843     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10844     * @return {CompositeElement} this
10845     */
10846     each : function(fn, scope){
10847         var els = this.elements;
10848         for(var i = 0, len = els.length; i < len; i++){
10849             if(fn.call(scope || els[i], els[i], this, i) === false) {
10850                 break;
10851             }
10852         }
10853         return this;
10854     },
10855
10856     /**
10857      * Returns the Element object at the specified index
10858      * @param {Number} index
10859      * @return {Roo.Element}
10860      */
10861     item : function(index){
10862         return this.elements[index] || null;
10863     },
10864
10865     /**
10866      * Returns the first Element
10867      * @return {Roo.Element}
10868      */
10869     first : function(){
10870         return this.item(0);
10871     },
10872
10873     /**
10874      * Returns the last Element
10875      * @return {Roo.Element}
10876      */
10877     last : function(){
10878         return this.item(this.elements.length-1);
10879     },
10880
10881     /**
10882      * Returns the number of elements in this composite
10883      * @return Number
10884      */
10885     getCount : function(){
10886         return this.elements.length;
10887     },
10888
10889     /**
10890      * Returns true if this composite contains the passed element
10891      * @return Boolean
10892      */
10893     contains : function(el){
10894         return this.indexOf(el) !== -1;
10895     },
10896
10897     /**
10898      * Returns true if this composite contains the passed element
10899      * @return Boolean
10900      */
10901     indexOf : function(el){
10902         return this.elements.indexOf(Roo.get(el));
10903     },
10904
10905
10906     /**
10907     * Removes the specified element(s).
10908     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10909     * or an array of any of those.
10910     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10911     * @return {CompositeElement} this
10912     */
10913     removeElement : function(el, removeDom){
10914         if(el instanceof Array){
10915             for(var i = 0, len = el.length; i < len; i++){
10916                 this.removeElement(el[i]);
10917             }
10918             return this;
10919         }
10920         var index = typeof el == 'number' ? el : this.indexOf(el);
10921         if(index !== -1){
10922             if(removeDom){
10923                 var d = this.elements[index];
10924                 if(d.dom){
10925                     d.remove();
10926                 }else{
10927                     d.parentNode.removeChild(d);
10928                 }
10929             }
10930             this.elements.splice(index, 1);
10931         }
10932         return this;
10933     },
10934
10935     /**
10936     * Replaces the specified element with the passed element.
10937     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10938     * to replace.
10939     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10940     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10941     * @return {CompositeElement} this
10942     */
10943     replaceElement : function(el, replacement, domReplace){
10944         var index = typeof el == 'number' ? el : this.indexOf(el);
10945         if(index !== -1){
10946             if(domReplace){
10947                 this.elements[index].replaceWith(replacement);
10948             }else{
10949                 this.elements.splice(index, 1, Roo.get(replacement))
10950             }
10951         }
10952         return this;
10953     },
10954
10955     /**
10956      * Removes all elements.
10957      */
10958     clear : function(){
10959         this.elements = [];
10960     }
10961 };
10962 (function(){
10963     Roo.CompositeElement.createCall = function(proto, fnName){
10964         if(!proto[fnName]){
10965             proto[fnName] = function(){
10966                 return this.invoke(fnName, arguments);
10967             };
10968         }
10969     };
10970     for(var fnName in Roo.Element.prototype){
10971         if(typeof Roo.Element.prototype[fnName] == "function"){
10972             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10973         }
10974     };
10975 })();
10976 /*
10977  * Based on:
10978  * Ext JS Library 1.1.1
10979  * Copyright(c) 2006-2007, Ext JS, LLC.
10980  *
10981  * Originally Released Under LGPL - original licence link has changed is not relivant.
10982  *
10983  * Fork - LGPL
10984  * <script type="text/javascript">
10985  */
10986
10987 /**
10988  * @class Roo.CompositeElementLite
10989  * @extends Roo.CompositeElement
10990  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10991  <pre><code>
10992  var els = Roo.select("#some-el div.some-class");
10993  // or select directly from an existing element
10994  var el = Roo.get('some-el');
10995  el.select('div.some-class');
10996
10997  els.setWidth(100); // all elements become 100 width
10998  els.hide(true); // all elements fade out and hide
10999  // or
11000  els.setWidth(100).hide(true);
11001  </code></pre><br><br>
11002  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11003  * actions will be performed on all the elements in this collection.</b>
11004  */
11005 Roo.CompositeElementLite = function(els){
11006     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11007     this.el = new Roo.Element.Flyweight();
11008 };
11009 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11010     addElements : function(els){
11011         if(els){
11012             if(els instanceof Array){
11013                 this.elements = this.elements.concat(els);
11014             }else{
11015                 var yels = this.elements;
11016                 var index = yels.length-1;
11017                 for(var i = 0, len = els.length; i < len; i++) {
11018                     yels[++index] = els[i];
11019                 }
11020             }
11021         }
11022         return this;
11023     },
11024     invoke : function(fn, args){
11025         var els = this.elements;
11026         var el = this.el;
11027         for(var i = 0, len = els.length; i < len; i++) {
11028             el.dom = els[i];
11029                 Roo.Element.prototype[fn].apply(el, args);
11030         }
11031         return this;
11032     },
11033     /**
11034      * Returns a flyweight Element of the dom element object at the specified index
11035      * @param {Number} index
11036      * @return {Roo.Element}
11037      */
11038     item : function(index){
11039         if(!this.elements[index]){
11040             return null;
11041         }
11042         this.el.dom = this.elements[index];
11043         return this.el;
11044     },
11045
11046     // fixes scope with flyweight
11047     addListener : function(eventName, handler, scope, opt){
11048         var els = this.elements;
11049         for(var i = 0, len = els.length; i < len; i++) {
11050             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11051         }
11052         return this;
11053     },
11054
11055     /**
11056     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11057     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11058     * a reference to the dom node, use el.dom.</b>
11059     * @param {Function} fn The function to call
11060     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11061     * @return {CompositeElement} this
11062     */
11063     each : function(fn, scope){
11064         var els = this.elements;
11065         var el = this.el;
11066         for(var i = 0, len = els.length; i < len; i++){
11067             el.dom = els[i];
11068                 if(fn.call(scope || el, el, this, i) === false){
11069                 break;
11070             }
11071         }
11072         return this;
11073     },
11074
11075     indexOf : function(el){
11076         return this.elements.indexOf(Roo.getDom(el));
11077     },
11078
11079     replaceElement : function(el, replacement, domReplace){
11080         var index = typeof el == 'number' ? el : this.indexOf(el);
11081         if(index !== -1){
11082             replacement = Roo.getDom(replacement);
11083             if(domReplace){
11084                 var d = this.elements[index];
11085                 d.parentNode.insertBefore(replacement, d);
11086                 d.parentNode.removeChild(d);
11087             }
11088             this.elements.splice(index, 1, replacement);
11089         }
11090         return this;
11091     }
11092 });
11093 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11094
11095 /*
11096  * Based on:
11097  * Ext JS Library 1.1.1
11098  * Copyright(c) 2006-2007, Ext JS, LLC.
11099  *
11100  * Originally Released Under LGPL - original licence link has changed is not relivant.
11101  *
11102  * Fork - LGPL
11103  * <script type="text/javascript">
11104  */
11105
11106  
11107
11108 /**
11109  * @class Roo.data.Connection
11110  * @extends Roo.util.Observable
11111  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11112  * either to a configured URL, or to a URL specified at request time.<br><br>
11113  * <p>
11114  * Requests made by this class are asynchronous, and will return immediately. No data from
11115  * the server will be available to the statement immediately following the {@link #request} call.
11116  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11117  * <p>
11118  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11119  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11120  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11121  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11122  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11123  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11124  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11125  * standard DOM methods.
11126  * @constructor
11127  * @param {Object} config a configuration object.
11128  */
11129 Roo.data.Connection = function(config){
11130     Roo.apply(this, config);
11131     this.addEvents({
11132         /**
11133          * @event beforerequest
11134          * Fires before a network request is made to retrieve a data object.
11135          * @param {Connection} conn This Connection object.
11136          * @param {Object} options The options config object passed to the {@link #request} method.
11137          */
11138         "beforerequest" : true,
11139         /**
11140          * @event requestcomplete
11141          * Fires if the request was successfully completed.
11142          * @param {Connection} conn This Connection object.
11143          * @param {Object} response The XHR object containing the response data.
11144          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11145          * @param {Object} options The options config object passed to the {@link #request} method.
11146          */
11147         "requestcomplete" : true,
11148         /**
11149          * @event requestexception
11150          * Fires if an error HTTP status was returned from the server.
11151          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11152          * @param {Connection} conn This Connection object.
11153          * @param {Object} response The XHR object containing the response data.
11154          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11155          * @param {Object} options The options config object passed to the {@link #request} method.
11156          */
11157         "requestexception" : true
11158     });
11159     Roo.data.Connection.superclass.constructor.call(this);
11160 };
11161
11162 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11163     /**
11164      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11165      */
11166     /**
11167      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11168      * extra parameters to each request made by this object. (defaults to undefined)
11169      */
11170     /**
11171      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11172      *  to each request made by this object. (defaults to undefined)
11173      */
11174     /**
11175      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11176      */
11177     /**
11178      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11179      */
11180     timeout : 30000,
11181     /**
11182      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11183      * @type Boolean
11184      */
11185     autoAbort:false,
11186
11187     /**
11188      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11189      * @type Boolean
11190      */
11191     disableCaching: true,
11192
11193     /**
11194      * Sends an HTTP request to a remote server.
11195      * @param {Object} options An object which may contain the following properties:<ul>
11196      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11197      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11198      * request, a url encoded string or a function to call to get either.</li>
11199      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11200      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11201      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11202      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11203      * <li>options {Object} The parameter to the request call.</li>
11204      * <li>success {Boolean} True if the request succeeded.</li>
11205      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11206      * </ul></li>
11207      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11208      * The callback is passed the following parameters:<ul>
11209      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11210      * <li>options {Object} The parameter to the request call.</li>
11211      * </ul></li>
11212      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11213      * The callback is passed the following parameters:<ul>
11214      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11215      * <li>options {Object} The parameter to the request call.</li>
11216      * </ul></li>
11217      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11218      * for the callback function. Defaults to the browser window.</li>
11219      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11220      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11221      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11222      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11223      * params for the post data. Any params will be appended to the URL.</li>
11224      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11225      * </ul>
11226      * @return {Number} transactionId
11227      */
11228     request : function(o){
11229         if(this.fireEvent("beforerequest", this, o) !== false){
11230             var p = o.params;
11231
11232             if(typeof p == "function"){
11233                 p = p.call(o.scope||window, o);
11234             }
11235             if(typeof p == "object"){
11236                 p = Roo.urlEncode(o.params);
11237             }
11238             if(this.extraParams){
11239                 var extras = Roo.urlEncode(this.extraParams);
11240                 p = p ? (p + '&' + extras) : extras;
11241             }
11242
11243             var url = o.url || this.url;
11244             if(typeof url == 'function'){
11245                 url = url.call(o.scope||window, o);
11246             }
11247
11248             if(o.form){
11249                 var form = Roo.getDom(o.form);
11250                 url = url || form.action;
11251
11252                 var enctype = form.getAttribute("enctype");
11253                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11254                     return this.doFormUpload(o, p, url);
11255                 }
11256                 var f = Roo.lib.Ajax.serializeForm(form);
11257                 p = p ? (p + '&' + f) : f;
11258             }
11259
11260             var hs = o.headers;
11261             if(this.defaultHeaders){
11262                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11263                 if(!o.headers){
11264                     o.headers = hs;
11265                 }
11266             }
11267
11268             var cb = {
11269                 success: this.handleResponse,
11270                 failure: this.handleFailure,
11271                 scope: this,
11272                 argument: {options: o},
11273                 timeout : this.timeout
11274             };
11275
11276             var method = o.method||this.method||(p ? "POST" : "GET");
11277
11278             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11279                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11280             }
11281
11282             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11283                 if(o.autoAbort){
11284                     this.abort();
11285                 }
11286             }else if(this.autoAbort !== false){
11287                 this.abort();
11288             }
11289
11290             if((method == 'GET' && p) || o.xmlData){
11291                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11292                 p = '';
11293             }
11294             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11295             return this.transId;
11296         }else{
11297             Roo.callback(o.callback, o.scope, [o, null, null]);
11298             return null;
11299         }
11300     },
11301
11302     /**
11303      * Determine whether this object has a request outstanding.
11304      * @param {Number} transactionId (Optional) defaults to the last transaction
11305      * @return {Boolean} True if there is an outstanding request.
11306      */
11307     isLoading : function(transId){
11308         if(transId){
11309             return Roo.lib.Ajax.isCallInProgress(transId);
11310         }else{
11311             return this.transId ? true : false;
11312         }
11313     },
11314
11315     /**
11316      * Aborts any outstanding request.
11317      * @param {Number} transactionId (Optional) defaults to the last transaction
11318      */
11319     abort : function(transId){
11320         if(transId || this.isLoading()){
11321             Roo.lib.Ajax.abort(transId || this.transId);
11322         }
11323     },
11324
11325     // private
11326     handleResponse : function(response){
11327         this.transId = false;
11328         var options = response.argument.options;
11329         response.argument = options ? options.argument : null;
11330         this.fireEvent("requestcomplete", this, response, options);
11331         Roo.callback(options.success, options.scope, [response, options]);
11332         Roo.callback(options.callback, options.scope, [options, true, response]);
11333     },
11334
11335     // private
11336     handleFailure : function(response, e){
11337         this.transId = false;
11338         var options = response.argument.options;
11339         response.argument = options ? options.argument : null;
11340         this.fireEvent("requestexception", this, response, options, e);
11341         Roo.callback(options.failure, options.scope, [response, options]);
11342         Roo.callback(options.callback, options.scope, [options, false, response]);
11343     },
11344
11345     // private
11346     doFormUpload : function(o, ps, url){
11347         var id = Roo.id();
11348         var frame = document.createElement('iframe');
11349         frame.id = id;
11350         frame.name = id;
11351         frame.className = 'x-hidden';
11352         if(Roo.isIE){
11353             frame.src = Roo.SSL_SECURE_URL;
11354         }
11355         document.body.appendChild(frame);
11356
11357         if(Roo.isIE){
11358            document.frames[id].name = id;
11359         }
11360
11361         var form = Roo.getDom(o.form);
11362         form.target = id;
11363         form.method = 'POST';
11364         form.enctype = form.encoding = 'multipart/form-data';
11365         if(url){
11366             form.action = url;
11367         }
11368
11369         var hiddens, hd;
11370         if(ps){ // add dynamic params
11371             hiddens = [];
11372             ps = Roo.urlDecode(ps, false);
11373             for(var k in ps){
11374                 if(ps.hasOwnProperty(k)){
11375                     hd = document.createElement('input');
11376                     hd.type = 'hidden';
11377                     hd.name = k;
11378                     hd.value = ps[k];
11379                     form.appendChild(hd);
11380                     hiddens.push(hd);
11381                 }
11382             }
11383         }
11384
11385         function cb(){
11386             var r = {  // bogus response object
11387                 responseText : '',
11388                 responseXML : null
11389             };
11390
11391             r.argument = o ? o.argument : null;
11392
11393             try { //
11394                 var doc;
11395                 if(Roo.isIE){
11396                     doc = frame.contentWindow.document;
11397                 }else {
11398                     doc = (frame.contentDocument || window.frames[id].document);
11399                 }
11400                 if(doc && doc.body){
11401                     r.responseText = doc.body.innerHTML;
11402                 }
11403                 if(doc && doc.XMLDocument){
11404                     r.responseXML = doc.XMLDocument;
11405                 }else {
11406                     r.responseXML = doc;
11407                 }
11408             }
11409             catch(e) {
11410                 // ignore
11411             }
11412
11413             Roo.EventManager.removeListener(frame, 'load', cb, this);
11414
11415             this.fireEvent("requestcomplete", this, r, o);
11416             Roo.callback(o.success, o.scope, [r, o]);
11417             Roo.callback(o.callback, o.scope, [o, true, r]);
11418
11419             setTimeout(function(){document.body.removeChild(frame);}, 100);
11420         }
11421
11422         Roo.EventManager.on(frame, 'load', cb, this);
11423         form.submit();
11424
11425         if(hiddens){ // remove dynamic params
11426             for(var i = 0, len = hiddens.length; i < len; i++){
11427                 form.removeChild(hiddens[i]);
11428             }
11429         }
11430     }
11431 });
11432
11433 /**
11434  * @class Roo.Ajax
11435  * @extends Roo.data.Connection
11436  * Global Ajax request class.
11437  *
11438  * @singleton
11439  */
11440 Roo.Ajax = new Roo.data.Connection({
11441     // fix up the docs
11442    /**
11443      * @cfg {String} url @hide
11444      */
11445     /**
11446      * @cfg {Object} extraParams @hide
11447      */
11448     /**
11449      * @cfg {Object} defaultHeaders @hide
11450      */
11451     /**
11452      * @cfg {String} method (Optional) @hide
11453      */
11454     /**
11455      * @cfg {Number} timeout (Optional) @hide
11456      */
11457     /**
11458      * @cfg {Boolean} autoAbort (Optional) @hide
11459      */
11460
11461     /**
11462      * @cfg {Boolean} disableCaching (Optional) @hide
11463      */
11464
11465     /**
11466      * @property  disableCaching
11467      * True to add a unique cache-buster param to GET requests. (defaults to true)
11468      * @type Boolean
11469      */
11470     /**
11471      * @property  url
11472      * The default URL to be used for requests to the server. (defaults to undefined)
11473      * @type String
11474      */
11475     /**
11476      * @property  extraParams
11477      * An object containing properties which are used as
11478      * extra parameters to each request made by this object. (defaults to undefined)
11479      * @type Object
11480      */
11481     /**
11482      * @property  defaultHeaders
11483      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11484      * @type Object
11485      */
11486     /**
11487      * @property  method
11488      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11489      * @type String
11490      */
11491     /**
11492      * @property  timeout
11493      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11494      * @type Number
11495      */
11496
11497     /**
11498      * @property  autoAbort
11499      * Whether a new request should abort any pending requests. (defaults to false)
11500      * @type Boolean
11501      */
11502     autoAbort : false,
11503
11504     /**
11505      * Serialize the passed form into a url encoded string
11506      * @param {String/HTMLElement} form
11507      * @return {String}
11508      */
11509     serializeForm : function(form){
11510         return Roo.lib.Ajax.serializeForm(form);
11511     }
11512 });/*
11513  * Based on:
11514  * Ext JS Library 1.1.1
11515  * Copyright(c) 2006-2007, Ext JS, LLC.
11516  *
11517  * Originally Released Under LGPL - original licence link has changed is not relivant.
11518  *
11519  * Fork - LGPL
11520  * <script type="text/javascript">
11521  */
11522  
11523 /**
11524  * @class Roo.Ajax
11525  * @extends Roo.data.Connection
11526  * Global Ajax request class.
11527  *
11528  * @instanceOf  Roo.data.Connection
11529  */
11530 Roo.Ajax = new Roo.data.Connection({
11531     // fix up the docs
11532     
11533     /**
11534      * fix up scoping
11535      * @scope Roo.Ajax
11536      */
11537     
11538    /**
11539      * @cfg {String} url @hide
11540      */
11541     /**
11542      * @cfg {Object} extraParams @hide
11543      */
11544     /**
11545      * @cfg {Object} defaultHeaders @hide
11546      */
11547     /**
11548      * @cfg {String} method (Optional) @hide
11549      */
11550     /**
11551      * @cfg {Number} timeout (Optional) @hide
11552      */
11553     /**
11554      * @cfg {Boolean} autoAbort (Optional) @hide
11555      */
11556
11557     /**
11558      * @cfg {Boolean} disableCaching (Optional) @hide
11559      */
11560
11561     /**
11562      * @property  disableCaching
11563      * True to add a unique cache-buster param to GET requests. (defaults to true)
11564      * @type Boolean
11565      */
11566     /**
11567      * @property  url
11568      * The default URL to be used for requests to the server. (defaults to undefined)
11569      * @type String
11570      */
11571     /**
11572      * @property  extraParams
11573      * An object containing properties which are used as
11574      * extra parameters to each request made by this object. (defaults to undefined)
11575      * @type Object
11576      */
11577     /**
11578      * @property  defaultHeaders
11579      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11580      * @type Object
11581      */
11582     /**
11583      * @property  method
11584      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11585      * @type String
11586      */
11587     /**
11588      * @property  timeout
11589      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11590      * @type Number
11591      */
11592
11593     /**
11594      * @property  autoAbort
11595      * Whether a new request should abort any pending requests. (defaults to false)
11596      * @type Boolean
11597      */
11598     autoAbort : false,
11599
11600     /**
11601      * Serialize the passed form into a url encoded string
11602      * @param {String/HTMLElement} form
11603      * @return {String}
11604      */
11605     serializeForm : function(form){
11606         return Roo.lib.Ajax.serializeForm(form);
11607     }
11608 });/*
11609  * Based on:
11610  * Ext JS Library 1.1.1
11611  * Copyright(c) 2006-2007, Ext JS, LLC.
11612  *
11613  * Originally Released Under LGPL - original licence link has changed is not relivant.
11614  *
11615  * Fork - LGPL
11616  * <script type="text/javascript">
11617  */
11618
11619  
11620 /**
11621  * @class Roo.UpdateManager
11622  * @extends Roo.util.Observable
11623  * Provides AJAX-style update for Element object.<br><br>
11624  * Usage:<br>
11625  * <pre><code>
11626  * // Get it from a Roo.Element object
11627  * var el = Roo.get("foo");
11628  * var mgr = el.getUpdateManager();
11629  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11630  * ...
11631  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11632  * <br>
11633  * // or directly (returns the same UpdateManager instance)
11634  * var mgr = new Roo.UpdateManager("myElementId");
11635  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11636  * mgr.on("update", myFcnNeedsToKnow);
11637  * <br>
11638    // short handed call directly from the element object
11639    Roo.get("foo").load({
11640         url: "bar.php",
11641         scripts:true,
11642         params: "for=bar",
11643         text: "Loading Foo..."
11644    });
11645  * </code></pre>
11646  * @constructor
11647  * Create new UpdateManager directly.
11648  * @param {String/HTMLElement/Roo.Element} el The element to update
11649  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11650  */
11651 Roo.UpdateManager = function(el, forceNew){
11652     el = Roo.get(el);
11653     if(!forceNew && el.updateManager){
11654         return el.updateManager;
11655     }
11656     /**
11657      * The Element object
11658      * @type Roo.Element
11659      */
11660     this.el = el;
11661     /**
11662      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11663      * @type String
11664      */
11665     this.defaultUrl = null;
11666
11667     this.addEvents({
11668         /**
11669          * @event beforeupdate
11670          * Fired before an update is made, return false from your handler and the update is cancelled.
11671          * @param {Roo.Element} el
11672          * @param {String/Object/Function} url
11673          * @param {String/Object} params
11674          */
11675         "beforeupdate": true,
11676         /**
11677          * @event update
11678          * Fired after successful update is made.
11679          * @param {Roo.Element} el
11680          * @param {Object} oResponseObject The response Object
11681          */
11682         "update": true,
11683         /**
11684          * @event failure
11685          * Fired on update failure.
11686          * @param {Roo.Element} el
11687          * @param {Object} oResponseObject The response Object
11688          */
11689         "failure": true
11690     });
11691     var d = Roo.UpdateManager.defaults;
11692     /**
11693      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11694      * @type String
11695      */
11696     this.sslBlankUrl = d.sslBlankUrl;
11697     /**
11698      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11699      * @type Boolean
11700      */
11701     this.disableCaching = d.disableCaching;
11702     /**
11703      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11704      * @type String
11705      */
11706     this.indicatorText = d.indicatorText;
11707     /**
11708      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11709      * @type String
11710      */
11711     this.showLoadIndicator = d.showLoadIndicator;
11712     /**
11713      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11714      * @type Number
11715      */
11716     this.timeout = d.timeout;
11717
11718     /**
11719      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11720      * @type Boolean
11721      */
11722     this.loadScripts = d.loadScripts;
11723
11724     /**
11725      * Transaction object of current executing transaction
11726      */
11727     this.transaction = null;
11728
11729     /**
11730      * @private
11731      */
11732     this.autoRefreshProcId = null;
11733     /**
11734      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11735      * @type Function
11736      */
11737     this.refreshDelegate = this.refresh.createDelegate(this);
11738     /**
11739      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11740      * @type Function
11741      */
11742     this.updateDelegate = this.update.createDelegate(this);
11743     /**
11744      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11745      * @type Function
11746      */
11747     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11748     /**
11749      * @private
11750      */
11751     this.successDelegate = this.processSuccess.createDelegate(this);
11752     /**
11753      * @private
11754      */
11755     this.failureDelegate = this.processFailure.createDelegate(this);
11756
11757     if(!this.renderer){
11758      /**
11759       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11760       */
11761     this.renderer = new Roo.UpdateManager.BasicRenderer();
11762     }
11763     
11764     Roo.UpdateManager.superclass.constructor.call(this);
11765 };
11766
11767 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11768     /**
11769      * Get the Element this UpdateManager is bound to
11770      * @return {Roo.Element} The element
11771      */
11772     getEl : function(){
11773         return this.el;
11774     },
11775     /**
11776      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11777      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11778 <pre><code>
11779 um.update({<br/>
11780     url: "your-url.php",<br/>
11781     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11782     callback: yourFunction,<br/>
11783     scope: yourObject, //(optional scope)  <br/>
11784     discardUrl: false, <br/>
11785     nocache: false,<br/>
11786     text: "Loading...",<br/>
11787     timeout: 30,<br/>
11788     scripts: false<br/>
11789 });
11790 </code></pre>
11791      * The only required property is url. The optional properties nocache, text and scripts
11792      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11793      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11794      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11795      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11796      */
11797     update : function(url, params, callback, discardUrl){
11798         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11799             var method = this.method, cfg;
11800             if(typeof url == "object"){ // must be config object
11801                 cfg = url;
11802                 url = cfg.url;
11803                 params = params || cfg.params;
11804                 callback = callback || cfg.callback;
11805                 discardUrl = discardUrl || cfg.discardUrl;
11806                 if(callback && cfg.scope){
11807                     callback = callback.createDelegate(cfg.scope);
11808                 }
11809                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11810                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11811                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11812                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11813                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11814             }
11815             this.showLoading();
11816             if(!discardUrl){
11817                 this.defaultUrl = url;
11818             }
11819             if(typeof url == "function"){
11820                 url = url.call(this);
11821             }
11822
11823             method = method || (params ? "POST" : "GET");
11824             if(method == "GET"){
11825                 url = this.prepareUrl(url);
11826             }
11827
11828             var o = Roo.apply(cfg ||{}, {
11829                 url : url,
11830                 params: params,
11831                 success: this.successDelegate,
11832                 failure: this.failureDelegate,
11833                 callback: undefined,
11834                 timeout: (this.timeout*1000),
11835                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11836             });
11837
11838             this.transaction = Roo.Ajax.request(o);
11839         }
11840     },
11841
11842     /**
11843      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11844      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11845      * @param {String/HTMLElement} form The form Id or form element
11846      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11847      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11848      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11849      */
11850     formUpdate : function(form, url, reset, callback){
11851         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855             form = Roo.getDom(form);
11856             this.transaction = Roo.Ajax.request({
11857                 form: form,
11858                 url:url,
11859                 success: this.successDelegate,
11860                 failure: this.failureDelegate,
11861                 timeout: (this.timeout*1000),
11862                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11863             });
11864             this.showLoading.defer(1, this);
11865         }
11866     },
11867
11868     /**
11869      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11870      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11871      */
11872     refresh : function(callback){
11873         if(this.defaultUrl == null){
11874             return;
11875         }
11876         this.update(this.defaultUrl, null, callback, true);
11877     },
11878
11879     /**
11880      * Set this element to auto refresh.
11881      * @param {Number} interval How often to update (in seconds).
11882      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11883      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11884      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11885      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11886      */
11887     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11888         if(refreshNow){
11889             this.update(url || this.defaultUrl, params, callback, true);
11890         }
11891         if(this.autoRefreshProcId){
11892             clearInterval(this.autoRefreshProcId);
11893         }
11894         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11895     },
11896
11897     /**
11898      * Stop auto refresh on this element.
11899      */
11900      stopAutoRefresh : function(){
11901         if(this.autoRefreshProcId){
11902             clearInterval(this.autoRefreshProcId);
11903             delete this.autoRefreshProcId;
11904         }
11905     },
11906
11907     isAutoRefreshing : function(){
11908        return this.autoRefreshProcId ? true : false;
11909     },
11910     /**
11911      * Called to update the element to "Loading" state. Override to perform custom action.
11912      */
11913     showLoading : function(){
11914         if(this.showLoadIndicator){
11915             this.el.update(this.indicatorText);
11916         }
11917     },
11918
11919     /**
11920      * Adds unique parameter to query string if disableCaching = true
11921      * @private
11922      */
11923     prepareUrl : function(url){
11924         if(this.disableCaching){
11925             var append = "_dc=" + (new Date().getTime());
11926             if(url.indexOf("?") !== -1){
11927                 url += "&" + append;
11928             }else{
11929                 url += "?" + append;
11930             }
11931         }
11932         return url;
11933     },
11934
11935     /**
11936      * @private
11937      */
11938     processSuccess : function(response){
11939         this.transaction = null;
11940         if(response.argument.form && response.argument.reset){
11941             try{ // put in try/catch since some older FF releases had problems with this
11942                 response.argument.form.reset();
11943             }catch(e){}
11944         }
11945         if(this.loadScripts){
11946             this.renderer.render(this.el, response, this,
11947                 this.updateComplete.createDelegate(this, [response]));
11948         }else{
11949             this.renderer.render(this.el, response, this);
11950             this.updateComplete(response);
11951         }
11952     },
11953
11954     updateComplete : function(response){
11955         this.fireEvent("update", this.el, response);
11956         if(typeof response.argument.callback == "function"){
11957             response.argument.callback(this.el, true, response);
11958         }
11959     },
11960
11961     /**
11962      * @private
11963      */
11964     processFailure : function(response){
11965         this.transaction = null;
11966         this.fireEvent("failure", this.el, response);
11967         if(typeof response.argument.callback == "function"){
11968             response.argument.callback(this.el, false, response);
11969         }
11970     },
11971
11972     /**
11973      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11974      * @param {Object} renderer The object implementing the render() method
11975      */
11976     setRenderer : function(renderer){
11977         this.renderer = renderer;
11978     },
11979
11980     getRenderer : function(){
11981        return this.renderer;
11982     },
11983
11984     /**
11985      * Set the defaultUrl used for updates
11986      * @param {String/Function} defaultUrl The url or a function to call to get the url
11987      */
11988     setDefaultUrl : function(defaultUrl){
11989         this.defaultUrl = defaultUrl;
11990     },
11991
11992     /**
11993      * Aborts the executing transaction
11994      */
11995     abort : function(){
11996         if(this.transaction){
11997             Roo.Ajax.abort(this.transaction);
11998         }
11999     },
12000
12001     /**
12002      * Returns true if an update is in progress
12003      * @return {Boolean}
12004      */
12005     isUpdating : function(){
12006         if(this.transaction){
12007             return Roo.Ajax.isLoading(this.transaction);
12008         }
12009         return false;
12010     }
12011 });
12012
12013 /**
12014  * @class Roo.UpdateManager.defaults
12015  * @static (not really - but it helps the doc tool)
12016  * The defaults collection enables customizing the default properties of UpdateManager
12017  */
12018    Roo.UpdateManager.defaults = {
12019        /**
12020          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12021          * @type Number
12022          */
12023          timeout : 30,
12024
12025          /**
12026          * True to process scripts by default (Defaults to false).
12027          * @type Boolean
12028          */
12029         loadScripts : false,
12030
12031         /**
12032         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12033         * @type String
12034         */
12035         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12036         /**
12037          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12038          * @type Boolean
12039          */
12040         disableCaching : false,
12041         /**
12042          * Whether to show indicatorText when loading (Defaults to true).
12043          * @type Boolean
12044          */
12045         showLoadIndicator : true,
12046         /**
12047          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12048          * @type String
12049          */
12050         indicatorText : '<div class="loading-indicator">Loading...</div>'
12051    };
12052
12053 /**
12054  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12055  *Usage:
12056  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12057  * @param {String/HTMLElement/Roo.Element} el The element to update
12058  * @param {String} url The url
12059  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12060  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12061  * @static
12062  * @deprecated
12063  * @member Roo.UpdateManager
12064  */
12065 Roo.UpdateManager.updateElement = function(el, url, params, options){
12066     var um = Roo.get(el, true).getUpdateManager();
12067     Roo.apply(um, options);
12068     um.update(url, params, options ? options.callback : null);
12069 };
12070 // alias for backwards compat
12071 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12072 /**
12073  * @class Roo.UpdateManager.BasicRenderer
12074  * Default Content renderer. Updates the elements innerHTML with the responseText.
12075  */
12076 Roo.UpdateManager.BasicRenderer = function(){};
12077
12078 Roo.UpdateManager.BasicRenderer.prototype = {
12079     /**
12080      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12081      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12082      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12083      * @param {Roo.Element} el The element being rendered
12084      * @param {Object} response The YUI Connect response object
12085      * @param {UpdateManager} updateManager The calling update manager
12086      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12087      */
12088      render : function(el, response, updateManager, callback){
12089         el.update(response.responseText, updateManager.loadScripts, callback);
12090     }
12091 };
12092 /*
12093  * Based on:
12094  * Ext JS Library 1.1.1
12095  * Copyright(c) 2006-2007, Ext JS, LLC.
12096  *
12097  * Originally Released Under LGPL - original licence link has changed is not relivant.
12098  *
12099  * Fork - LGPL
12100  * <script type="text/javascript">
12101  */
12102
12103 /**
12104  * @class Roo.util.DelayedTask
12105  * Provides a convenient method of performing setTimeout where a new
12106  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12107  * You can use this class to buffer
12108  * the keypress events for a certain number of milliseconds, and perform only if they stop
12109  * for that amount of time.
12110  * @constructor The parameters to this constructor serve as defaults and are not required.
12111  * @param {Function} fn (optional) The default function to timeout
12112  * @param {Object} scope (optional) The default scope of that timeout
12113  * @param {Array} args (optional) The default Array of arguments
12114  */
12115 Roo.util.DelayedTask = function(fn, scope, args){
12116     var id = null, d, t;
12117
12118     var call = function(){
12119         var now = new Date().getTime();
12120         if(now - t >= d){
12121             clearInterval(id);
12122             id = null;
12123             fn.apply(scope, args || []);
12124         }
12125     };
12126     /**
12127      * Cancels any pending timeout and queues a new one
12128      * @param {Number} delay The milliseconds to delay
12129      * @param {Function} newFn (optional) Overrides function passed to constructor
12130      * @param {Object} newScope (optional) Overrides scope passed to constructor
12131      * @param {Array} newArgs (optional) Overrides args passed to constructor
12132      */
12133     this.delay = function(delay, newFn, newScope, newArgs){
12134         if(id && delay != d){
12135             this.cancel();
12136         }
12137         d = delay;
12138         t = new Date().getTime();
12139         fn = newFn || fn;
12140         scope = newScope || scope;
12141         args = newArgs || args;
12142         if(!id){
12143             id = setInterval(call, d);
12144         }
12145     };
12146
12147     /**
12148      * Cancel the last queued timeout
12149      */
12150     this.cancel = function(){
12151         if(id){
12152             clearInterval(id);
12153             id = null;
12154         }
12155     };
12156 };/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166  
12167  
12168 Roo.util.TaskRunner = function(interval){
12169     interval = interval || 10;
12170     var tasks = [], removeQueue = [];
12171     var id = 0;
12172     var running = false;
12173
12174     var stopThread = function(){
12175         running = false;
12176         clearInterval(id);
12177         id = 0;
12178     };
12179
12180     var startThread = function(){
12181         if(!running){
12182             running = true;
12183             id = setInterval(runTasks, interval);
12184         }
12185     };
12186
12187     var removeTask = function(task){
12188         removeQueue.push(task);
12189         if(task.onStop){
12190             task.onStop();
12191         }
12192     };
12193
12194     var runTasks = function(){
12195         if(removeQueue.length > 0){
12196             for(var i = 0, len = removeQueue.length; i < len; i++){
12197                 tasks.remove(removeQueue[i]);
12198             }
12199             removeQueue = [];
12200             if(tasks.length < 1){
12201                 stopThread();
12202                 return;
12203             }
12204         }
12205         var now = new Date().getTime();
12206         for(var i = 0, len = tasks.length; i < len; ++i){
12207             var t = tasks[i];
12208             var itime = now - t.taskRunTime;
12209             if(t.interval <= itime){
12210                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12211                 t.taskRunTime = now;
12212                 if(rt === false || t.taskRunCount === t.repeat){
12213                     removeTask(t);
12214                     return;
12215                 }
12216             }
12217             if(t.duration && t.duration <= (now - t.taskStartTime)){
12218                 removeTask(t);
12219             }
12220         }
12221     };
12222
12223     /**
12224      * Queues a new task.
12225      * @param {Object} task
12226      */
12227     this.start = function(task){
12228         tasks.push(task);
12229         task.taskStartTime = new Date().getTime();
12230         task.taskRunTime = 0;
12231         task.taskRunCount = 0;
12232         startThread();
12233         return task;
12234     };
12235
12236     this.stop = function(task){
12237         removeTask(task);
12238         return task;
12239     };
12240
12241     this.stopAll = function(){
12242         stopThread();
12243         for(var i = 0, len = tasks.length; i < len; i++){
12244             if(tasks[i].onStop){
12245                 tasks[i].onStop();
12246             }
12247         }
12248         tasks = [];
12249         removeQueue = [];
12250     };
12251 };
12252
12253 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12254  * Based on:
12255  * Ext JS Library 1.1.1
12256  * Copyright(c) 2006-2007, Ext JS, LLC.
12257  *
12258  * Originally Released Under LGPL - original licence link has changed is not relivant.
12259  *
12260  * Fork - LGPL
12261  * <script type="text/javascript">
12262  */
12263
12264  
12265 /**
12266  * @class Roo.util.MixedCollection
12267  * @extends Roo.util.Observable
12268  * A Collection class that maintains both numeric indexes and keys and exposes events.
12269  * @constructor
12270  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12271  * collection (defaults to false)
12272  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12273  * and return the key value for that item.  This is used when available to look up the key on items that
12274  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12275  * equivalent to providing an implementation for the {@link #getKey} method.
12276  */
12277 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12278     this.items = [];
12279     this.map = {};
12280     this.keys = [];
12281     this.length = 0;
12282     this.addEvents({
12283         /**
12284          * @event clear
12285          * Fires when the collection is cleared.
12286          */
12287         "clear" : true,
12288         /**
12289          * @event add
12290          * Fires when an item is added to the collection.
12291          * @param {Number} index The index at which the item was added.
12292          * @param {Object} o The item added.
12293          * @param {String} key The key associated with the added item.
12294          */
12295         "add" : true,
12296         /**
12297          * @event replace
12298          * Fires when an item is replaced in the collection.
12299          * @param {String} key he key associated with the new added.
12300          * @param {Object} old The item being replaced.
12301          * @param {Object} new The new item.
12302          */
12303         "replace" : true,
12304         /**
12305          * @event remove
12306          * Fires when an item is removed from the collection.
12307          * @param {Object} o The item being removed.
12308          * @param {String} key (optional) The key associated with the removed item.
12309          */
12310         "remove" : true,
12311         "sort" : true
12312     });
12313     this.allowFunctions = allowFunctions === true;
12314     if(keyFn){
12315         this.getKey = keyFn;
12316     }
12317     Roo.util.MixedCollection.superclass.constructor.call(this);
12318 };
12319
12320 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12321     allowFunctions : false,
12322     
12323 /**
12324  * Adds an item to the collection.
12325  * @param {String} key The key to associate with the item
12326  * @param {Object} o The item to add.
12327  * @return {Object} The item added.
12328  */
12329     add : function(key, o){
12330         if(arguments.length == 1){
12331             o = arguments[0];
12332             key = this.getKey(o);
12333         }
12334         if(typeof key == "undefined" || key === null){
12335             this.length++;
12336             this.items.push(o);
12337             this.keys.push(null);
12338         }else{
12339             var old = this.map[key];
12340             if(old){
12341                 return this.replace(key, o);
12342             }
12343             this.length++;
12344             this.items.push(o);
12345             this.map[key] = o;
12346             this.keys.push(key);
12347         }
12348         this.fireEvent("add", this.length-1, o, key);
12349         return o;
12350     },
12351        
12352 /**
12353   * MixedCollection has a generic way to fetch keys if you implement getKey.
12354 <pre><code>
12355 // normal way
12356 var mc = new Roo.util.MixedCollection();
12357 mc.add(someEl.dom.id, someEl);
12358 mc.add(otherEl.dom.id, otherEl);
12359 //and so on
12360
12361 // using getKey
12362 var mc = new Roo.util.MixedCollection();
12363 mc.getKey = function(el){
12364    return el.dom.id;
12365 };
12366 mc.add(someEl);
12367 mc.add(otherEl);
12368
12369 // or via the constructor
12370 var mc = new Roo.util.MixedCollection(false, function(el){
12371    return el.dom.id;
12372 });
12373 mc.add(someEl);
12374 mc.add(otherEl);
12375 </code></pre>
12376  * @param o {Object} The item for which to find the key.
12377  * @return {Object} The key for the passed item.
12378  */
12379     getKey : function(o){
12380          return o.id; 
12381     },
12382    
12383 /**
12384  * Replaces an item in the collection.
12385  * @param {String} key The key associated with the item to replace, or the item to replace.
12386  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12387  * @return {Object}  The new item.
12388  */
12389     replace : function(key, o){
12390         if(arguments.length == 1){
12391             o = arguments[0];
12392             key = this.getKey(o);
12393         }
12394         var old = this.item(key);
12395         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12396              return this.add(key, o);
12397         }
12398         var index = this.indexOfKey(key);
12399         this.items[index] = o;
12400         this.map[key] = o;
12401         this.fireEvent("replace", key, old, o);
12402         return o;
12403     },
12404    
12405 /**
12406  * Adds all elements of an Array or an Object to the collection.
12407  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12408  * an Array of values, each of which are added to the collection.
12409  */
12410     addAll : function(objs){
12411         if(arguments.length > 1 || objs instanceof Array){
12412             var args = arguments.length > 1 ? arguments : objs;
12413             for(var i = 0, len = args.length; i < len; i++){
12414                 this.add(args[i]);
12415             }
12416         }else{
12417             for(var key in objs){
12418                 if(this.allowFunctions || typeof objs[key] != "function"){
12419                     this.add(key, objs[key]);
12420                 }
12421             }
12422         }
12423     },
12424    
12425 /**
12426  * Executes the specified function once for every item in the collection, passing each
12427  * item as the first and only parameter. returning false from the function will stop the iteration.
12428  * @param {Function} fn The function to execute for each item.
12429  * @param {Object} scope (optional) The scope in which to execute the function.
12430  */
12431     each : function(fn, scope){
12432         var items = [].concat(this.items); // each safe for removal
12433         for(var i = 0, len = items.length; i < len; i++){
12434             if(fn.call(scope || items[i], items[i], i, len) === false){
12435                 break;
12436             }
12437         }
12438     },
12439    
12440 /**
12441  * Executes the specified function once for every key in the collection, passing each
12442  * key, and its associated item as the first two parameters.
12443  * @param {Function} fn The function to execute for each item.
12444  * @param {Object} scope (optional) The scope in which to execute the function.
12445  */
12446     eachKey : function(fn, scope){
12447         for(var i = 0, len = this.keys.length; i < len; i++){
12448             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12449         }
12450     },
12451    
12452 /**
12453  * Returns the first item in the collection which elicits a true return value from the
12454  * passed selection function.
12455  * @param {Function} fn The selection function to execute for each item.
12456  * @param {Object} scope (optional) The scope in which to execute the function.
12457  * @return {Object} The first item in the collection which returned true from the selection function.
12458  */
12459     find : function(fn, scope){
12460         for(var i = 0, len = this.items.length; i < len; i++){
12461             if(fn.call(scope || window, this.items[i], this.keys[i])){
12462                 return this.items[i];
12463             }
12464         }
12465         return null;
12466     },
12467    
12468 /**
12469  * Inserts an item at the specified index in the collection.
12470  * @param {Number} index The index to insert the item at.
12471  * @param {String} key The key to associate with the new item, or the item itself.
12472  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12473  * @return {Object} The item inserted.
12474  */
12475     insert : function(index, key, o){
12476         if(arguments.length == 2){
12477             o = arguments[1];
12478             key = this.getKey(o);
12479         }
12480         if(index >= this.length){
12481             return this.add(key, o);
12482         }
12483         this.length++;
12484         this.items.splice(index, 0, o);
12485         if(typeof key != "undefined" && key != null){
12486             this.map[key] = o;
12487         }
12488         this.keys.splice(index, 0, key);
12489         this.fireEvent("add", index, o, key);
12490         return o;
12491     },
12492    
12493 /**
12494  * Removed an item from the collection.
12495  * @param {Object} o The item to remove.
12496  * @return {Object} The item removed.
12497  */
12498     remove : function(o){
12499         return this.removeAt(this.indexOf(o));
12500     },
12501    
12502 /**
12503  * Remove an item from a specified index in the collection.
12504  * @param {Number} index The index within the collection of the item to remove.
12505  */
12506     removeAt : function(index){
12507         if(index < this.length && index >= 0){
12508             this.length--;
12509             var o = this.items[index];
12510             this.items.splice(index, 1);
12511             var key = this.keys[index];
12512             if(typeof key != "undefined"){
12513                 delete this.map[key];
12514             }
12515             this.keys.splice(index, 1);
12516             this.fireEvent("remove", o, key);
12517         }
12518     },
12519    
12520 /**
12521  * Removed an item associated with the passed key fom the collection.
12522  * @param {String} key The key of the item to remove.
12523  */
12524     removeKey : function(key){
12525         return this.removeAt(this.indexOfKey(key));
12526     },
12527    
12528 /**
12529  * Returns the number of items in the collection.
12530  * @return {Number} the number of items in the collection.
12531  */
12532     getCount : function(){
12533         return this.length; 
12534     },
12535    
12536 /**
12537  * Returns index within the collection of the passed Object.
12538  * @param {Object} o The item to find the index of.
12539  * @return {Number} index of the item.
12540  */
12541     indexOf : function(o){
12542         if(!this.items.indexOf){
12543             for(var i = 0, len = this.items.length; i < len; i++){
12544                 if(this.items[i] == o) return i;
12545             }
12546             return -1;
12547         }else{
12548             return this.items.indexOf(o);
12549         }
12550     },
12551    
12552 /**
12553  * Returns index within the collection of the passed key.
12554  * @param {String} key The key to find the index of.
12555  * @return {Number} index of the key.
12556  */
12557     indexOfKey : function(key){
12558         if(!this.keys.indexOf){
12559             for(var i = 0, len = this.keys.length; i < len; i++){
12560                 if(this.keys[i] == key) return i;
12561             }
12562             return -1;
12563         }else{
12564             return this.keys.indexOf(key);
12565         }
12566     },
12567    
12568 /**
12569  * Returns the item associated with the passed key OR index. Key has priority over index.
12570  * @param {String/Number} key The key or index of the item.
12571  * @return {Object} The item associated with the passed key.
12572  */
12573     item : function(key){
12574         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12575         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12576     },
12577     
12578 /**
12579  * Returns the item at the specified index.
12580  * @param {Number} index The index of the item.
12581  * @return {Object}
12582  */
12583     itemAt : function(index){
12584         return this.items[index];
12585     },
12586     
12587 /**
12588  * Returns the item associated with the passed key.
12589  * @param {String/Number} key The key of the item.
12590  * @return {Object} The item associated with the passed key.
12591  */
12592     key : function(key){
12593         return this.map[key];
12594     },
12595    
12596 /**
12597  * Returns true if the collection contains the passed Object as an item.
12598  * @param {Object} o  The Object to look for in the collection.
12599  * @return {Boolean} True if the collection contains the Object as an item.
12600  */
12601     contains : function(o){
12602         return this.indexOf(o) != -1;
12603     },
12604    
12605 /**
12606  * Returns true if the collection contains the passed Object as a key.
12607  * @param {String} key The key to look for in the collection.
12608  * @return {Boolean} True if the collection contains the Object as a key.
12609  */
12610     containsKey : function(key){
12611         return typeof this.map[key] != "undefined";
12612     },
12613    
12614 /**
12615  * Removes all items from the collection.
12616  */
12617     clear : function(){
12618         this.length = 0;
12619         this.items = [];
12620         this.keys = [];
12621         this.map = {};
12622         this.fireEvent("clear");
12623     },
12624    
12625 /**
12626  * Returns the first item in the collection.
12627  * @return {Object} the first item in the collection..
12628  */
12629     first : function(){
12630         return this.items[0]; 
12631     },
12632    
12633 /**
12634  * Returns the last item in the collection.
12635  * @return {Object} the last item in the collection..
12636  */
12637     last : function(){
12638         return this.items[this.length-1];   
12639     },
12640     
12641     _sort : function(property, dir, fn){
12642         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12643         fn = fn || function(a, b){
12644             return a-b;
12645         };
12646         var c = [], k = this.keys, items = this.items;
12647         for(var i = 0, len = items.length; i < len; i++){
12648             c[c.length] = {key: k[i], value: items[i], index: i};
12649         }
12650         c.sort(function(a, b){
12651             var v = fn(a[property], b[property]) * dsc;
12652             if(v == 0){
12653                 v = (a.index < b.index ? -1 : 1);
12654             }
12655             return v;
12656         });
12657         for(var i = 0, len = c.length; i < len; i++){
12658             items[i] = c[i].value;
12659             k[i] = c[i].key;
12660         }
12661         this.fireEvent("sort", this);
12662     },
12663     
12664     /**
12665      * Sorts this collection with the passed comparison function
12666      * @param {String} direction (optional) "ASC" or "DESC"
12667      * @param {Function} fn (optional) comparison function
12668      */
12669     sort : function(dir, fn){
12670         this._sort("value", dir, fn);
12671     },
12672     
12673     /**
12674      * Sorts this collection by keys
12675      * @param {String} direction (optional) "ASC" or "DESC"
12676      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12677      */
12678     keySort : function(dir, fn){
12679         this._sort("key", dir, fn || function(a, b){
12680             return String(a).toUpperCase()-String(b).toUpperCase();
12681         });
12682     },
12683     
12684     /**
12685      * Returns a range of items in this collection
12686      * @param {Number} startIndex (optional) defaults to 0
12687      * @param {Number} endIndex (optional) default to the last item
12688      * @return {Array} An array of items
12689      */
12690     getRange : function(start, end){
12691         var items = this.items;
12692         if(items.length < 1){
12693             return [];
12694         }
12695         start = start || 0;
12696         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12697         var r = [];
12698         if(start <= end){
12699             for(var i = start; i <= end; i++) {
12700                     r[r.length] = items[i];
12701             }
12702         }else{
12703             for(var i = start; i >= end; i--) {
12704                     r[r.length] = items[i];
12705             }
12706         }
12707         return r;
12708     },
12709         
12710     /**
12711      * Filter the <i>objects</i> in this collection by a specific property. 
12712      * Returns a new collection that has been filtered.
12713      * @param {String} property A property on your objects
12714      * @param {String/RegExp} value Either string that the property values 
12715      * should start with or a RegExp to test against the property
12716      * @return {MixedCollection} The new filtered collection
12717      */
12718     filter : function(property, value){
12719         if(!value.exec){ // not a regex
12720             value = String(value);
12721             if(value.length == 0){
12722                 return this.clone();
12723             }
12724             value = new RegExp("^" + Roo.escapeRe(value), "i");
12725         }
12726         return this.filterBy(function(o){
12727             return o && value.test(o[property]);
12728         });
12729         },
12730     
12731     /**
12732      * Filter by a function. * Returns a new collection that has been filtered.
12733      * The passed function will be called with each 
12734      * object in the collection. If the function returns true, the value is included 
12735      * otherwise it is filtered.
12736      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12737      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12738      * @return {MixedCollection} The new filtered collection
12739      */
12740     filterBy : function(fn, scope){
12741         var r = new Roo.util.MixedCollection();
12742         r.getKey = this.getKey;
12743         var k = this.keys, it = this.items;
12744         for(var i = 0, len = it.length; i < len; i++){
12745             if(fn.call(scope||this, it[i], k[i])){
12746                                 r.add(k[i], it[i]);
12747                         }
12748         }
12749         return r;
12750     },
12751     
12752     /**
12753      * Creates a duplicate of this collection
12754      * @return {MixedCollection}
12755      */
12756     clone : function(){
12757         var r = new Roo.util.MixedCollection();
12758         var k = this.keys, it = this.items;
12759         for(var i = 0, len = it.length; i < len; i++){
12760             r.add(k[i], it[i]);
12761         }
12762         r.getKey = this.getKey;
12763         return r;
12764     }
12765 });
12766 /**
12767  * Returns the item associated with the passed key or index.
12768  * @method
12769  * @param {String/Number} key The key or index of the item.
12770  * @return {Object} The item associated with the passed key.
12771  */
12772 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12773  * Based on:
12774  * Ext JS Library 1.1.1
12775  * Copyright(c) 2006-2007, Ext JS, LLC.
12776  *
12777  * Originally Released Under LGPL - original licence link has changed is not relivant.
12778  *
12779  * Fork - LGPL
12780  * <script type="text/javascript">
12781  */
12782 /**
12783  * @class Roo.util.JSON
12784  * Modified version of Douglas Crockford"s json.js that doesn"t
12785  * mess with the Object prototype 
12786  * http://www.json.org/js.html
12787  * @singleton
12788  */
12789 Roo.util.JSON = new (function(){
12790     var useHasOwn = {}.hasOwnProperty ? true : false;
12791     
12792     // crashes Safari in some instances
12793     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12794     
12795     var pad = function(n) {
12796         return n < 10 ? "0" + n : n;
12797     };
12798     
12799     var m = {
12800         "\b": '\\b',
12801         "\t": '\\t',
12802         "\n": '\\n',
12803         "\f": '\\f',
12804         "\r": '\\r',
12805         '"' : '\\"',
12806         "\\": '\\\\'
12807     };
12808
12809     var encodeString = function(s){
12810         if (/["\\\x00-\x1f]/.test(s)) {
12811             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12812                 var c = m[b];
12813                 if(c){
12814                     return c;
12815                 }
12816                 c = b.charCodeAt();
12817                 return "\\u00" +
12818                     Math.floor(c / 16).toString(16) +
12819                     (c % 16).toString(16);
12820             }) + '"';
12821         }
12822         return '"' + s + '"';
12823     };
12824     
12825     var encodeArray = function(o){
12826         var a = ["["], b, i, l = o.length, v;
12827             for (i = 0; i < l; i += 1) {
12828                 v = o[i];
12829                 switch (typeof v) {
12830                     case "undefined":
12831                     case "function":
12832                     case "unknown":
12833                         break;
12834                     default:
12835                         if (b) {
12836                             a.push(',');
12837                         }
12838                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12839                         b = true;
12840                 }
12841             }
12842             a.push("]");
12843             return a.join("");
12844     };
12845     
12846     var encodeDate = function(o){
12847         return '"' + o.getFullYear() + "-" +
12848                 pad(o.getMonth() + 1) + "-" +
12849                 pad(o.getDate()) + "T" +
12850                 pad(o.getHours()) + ":" +
12851                 pad(o.getMinutes()) + ":" +
12852                 pad(o.getSeconds()) + '"';
12853     };
12854     
12855     /**
12856      * Encodes an Object, Array or other value
12857      * @param {Mixed} o The variable to encode
12858      * @return {String} The JSON string
12859      */
12860     this.encode = function(o)
12861     {
12862         // should this be extended to fully wrap stringify..
12863         
12864         if(typeof o == "undefined" || o === null){
12865             return "null";
12866         }else if(o instanceof Array){
12867             return encodeArray(o);
12868         }else if(o instanceof Date){
12869             return encodeDate(o);
12870         }else if(typeof o == "string"){
12871             return encodeString(o);
12872         }else if(typeof o == "number"){
12873             return isFinite(o) ? String(o) : "null";
12874         }else if(typeof o == "boolean"){
12875             return String(o);
12876         }else {
12877             var a = ["{"], b, i, v;
12878             for (i in o) {
12879                 if(!useHasOwn || o.hasOwnProperty(i)) {
12880                     v = o[i];
12881                     switch (typeof v) {
12882                     case "undefined":
12883                     case "function":
12884                     case "unknown":
12885                         break;
12886                     default:
12887                         if(b){
12888                             a.push(',');
12889                         }
12890                         a.push(this.encode(i), ":",
12891                                 v === null ? "null" : this.encode(v));
12892                         b = true;
12893                     }
12894                 }
12895             }
12896             a.push("}");
12897             return a.join("");
12898         }
12899     };
12900     
12901     /**
12902      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12903      * @param {String} json The JSON string
12904      * @return {Object} The resulting object
12905      */
12906     this.decode = function(json){
12907         
12908         return  /** eval:var:json */ eval("(" + json + ')');
12909     };
12910 })();
12911 /** 
12912  * Shorthand for {@link Roo.util.JSON#encode}
12913  * @member Roo encode 
12914  * @method */
12915 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12916 /** 
12917  * Shorthand for {@link Roo.util.JSON#decode}
12918  * @member Roo decode 
12919  * @method */
12920 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12921 /*
12922  * Based on:
12923  * Ext JS Library 1.1.1
12924  * Copyright(c) 2006-2007, Ext JS, LLC.
12925  *
12926  * Originally Released Under LGPL - original licence link has changed is not relivant.
12927  *
12928  * Fork - LGPL
12929  * <script type="text/javascript">
12930  */
12931  
12932 /**
12933  * @class Roo.util.Format
12934  * Reusable data formatting functions
12935  * @singleton
12936  */
12937 Roo.util.Format = function(){
12938     var trimRe = /^\s+|\s+$/g;
12939     return {
12940         /**
12941          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12942          * @param {String} value The string to truncate
12943          * @param {Number} length The maximum length to allow before truncating
12944          * @return {String} The converted text
12945          */
12946         ellipsis : function(value, len){
12947             if(value && value.length > len){
12948                 return value.substr(0, len-3)+"...";
12949             }
12950             return value;
12951         },
12952
12953         /**
12954          * Checks a reference and converts it to empty string if it is undefined
12955          * @param {Mixed} value Reference to check
12956          * @return {Mixed} Empty string if converted, otherwise the original value
12957          */
12958         undef : function(value){
12959             return typeof value != "undefined" ? value : "";
12960         },
12961
12962         /**
12963          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12964          * @param {String} value The string to encode
12965          * @return {String} The encoded text
12966          */
12967         htmlEncode : function(value){
12968             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12969         },
12970
12971         /**
12972          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12973          * @param {String} value The string to decode
12974          * @return {String} The decoded text
12975          */
12976         htmlDecode : function(value){
12977             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12978         },
12979
12980         /**
12981          * Trims any whitespace from either side of a string
12982          * @param {String} value The text to trim
12983          * @return {String} The trimmed text
12984          */
12985         trim : function(value){
12986             return String(value).replace(trimRe, "");
12987         },
12988
12989         /**
12990          * Returns a substring from within an original string
12991          * @param {String} value The original text
12992          * @param {Number} start The start index of the substring
12993          * @param {Number} length The length of the substring
12994          * @return {String} The substring
12995          */
12996         substr : function(value, start, length){
12997             return String(value).substr(start, length);
12998         },
12999
13000         /**
13001          * Converts a string to all lower case letters
13002          * @param {String} value The text to convert
13003          * @return {String} The converted text
13004          */
13005         lowercase : function(value){
13006             return String(value).toLowerCase();
13007         },
13008
13009         /**
13010          * Converts a string to all upper case letters
13011          * @param {String} value The text to convert
13012          * @return {String} The converted text
13013          */
13014         uppercase : function(value){
13015             return String(value).toUpperCase();
13016         },
13017
13018         /**
13019          * Converts the first character only of a string to upper case
13020          * @param {String} value The text to convert
13021          * @return {String} The converted text
13022          */
13023         capitalize : function(value){
13024             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13025         },
13026
13027         // private
13028         call : function(value, fn){
13029             if(arguments.length > 2){
13030                 var args = Array.prototype.slice.call(arguments, 2);
13031                 args.unshift(value);
13032                  
13033                 return /** eval:var:value */  eval(fn).apply(window, args);
13034             }else{
13035                 /** eval:var:value */
13036                 return /** eval:var:value */ eval(fn).call(window, value);
13037             }
13038         },
13039
13040        
13041         /**
13042          * safer version of Math.toFixed..??/
13043          * @param {Number/String} value The numeric value to format
13044          * @param {Number/String} value Decimal places 
13045          * @return {String} The formatted currency string
13046          */
13047         toFixed : function(v, n)
13048         {
13049             // why not use to fixed - precision is buggered???
13050             if (!n) {
13051                 return Math.round(v-0);
13052             }
13053             var fact = Math.pow(10,n+1);
13054             v = (Math.round((v-0)*fact))/fact;
13055             var z = (''+fact).substring(2);
13056             if (v == Math.floor(v)) {
13057                 return Math.floor(v) + '.' + z;
13058             }
13059             
13060             // now just padd decimals..
13061             var ps = String(v).split('.');
13062             var fd = (ps[1] + z);
13063             var r = fd.substring(0,n); 
13064             var rm = fd.substring(n); 
13065             if (rm < 5) {
13066                 return ps[0] + '.' + r;
13067             }
13068             r*=1; // turn it into a number;
13069             r++;
13070             if (String(r).length != n) {
13071                 ps[0]*=1;
13072                 ps[0]++;
13073                 r = String(r).substring(1); // chop the end off.
13074             }
13075             
13076             return ps[0] + '.' + r;
13077              
13078         },
13079         
13080         /**
13081          * Format a number as US currency
13082          * @param {Number/String} value The numeric value to format
13083          * @return {String} The formatted currency string
13084          */
13085         usMoney : function(v){
13086             v = (Math.round((v-0)*100))/100;
13087             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13088             v = String(v);
13089             var ps = v.split('.');
13090             var whole = ps[0];
13091             var sub = ps[1] ? '.'+ ps[1] : '.00';
13092             var r = /(\d+)(\d{3})/;
13093             while (r.test(whole)) {
13094                 whole = whole.replace(r, '$1' + ',' + '$2');
13095             }
13096             return "$" + whole + sub ;
13097         },
13098         
13099         /**
13100          * Parse a value into a formatted date using the specified format pattern.
13101          * @param {Mixed} value The value to format
13102          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13103          * @return {String} The formatted date string
13104          */
13105         date : function(v, format){
13106             if(!v){
13107                 return "";
13108             }
13109             if(!(v instanceof Date)){
13110                 v = new Date(Date.parse(v));
13111             }
13112             return v.dateFormat(format || "m/d/Y");
13113         },
13114
13115         /**
13116          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13117          * @param {String} format Any valid date format string
13118          * @return {Function} The date formatting function
13119          */
13120         dateRenderer : function(format){
13121             return function(v){
13122                 return Roo.util.Format.date(v, format);  
13123             };
13124         },
13125
13126         // private
13127         stripTagsRE : /<\/?[^>]+>/gi,
13128         
13129         /**
13130          * Strips all HTML tags
13131          * @param {Mixed} value The text from which to strip tags
13132          * @return {String} The stripped text
13133          */
13134         stripTags : function(v){
13135             return !v ? v : String(v).replace(this.stripTagsRE, "");
13136         }
13137     };
13138 }();/*
13139  * Based on:
13140  * Ext JS Library 1.1.1
13141  * Copyright(c) 2006-2007, Ext JS, LLC.
13142  *
13143  * Originally Released Under LGPL - original licence link has changed is not relivant.
13144  *
13145  * Fork - LGPL
13146  * <script type="text/javascript">
13147  */
13148
13149
13150  
13151
13152 /**
13153  * @class Roo.MasterTemplate
13154  * @extends Roo.Template
13155  * Provides a template that can have child templates. The syntax is:
13156 <pre><code>
13157 var t = new Roo.MasterTemplate(
13158         '&lt;select name="{name}"&gt;',
13159                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13160         '&lt;/select&gt;'
13161 );
13162 t.add('options', {value: 'foo', text: 'bar'});
13163 // or you can add multiple child elements in one shot
13164 t.addAll('options', [
13165     {value: 'foo', text: 'bar'},
13166     {value: 'foo2', text: 'bar2'},
13167     {value: 'foo3', text: 'bar3'}
13168 ]);
13169 // then append, applying the master template values
13170 t.append('my-form', {name: 'my-select'});
13171 </code></pre>
13172 * A name attribute for the child template is not required if you have only one child
13173 * template or you want to refer to them by index.
13174  */
13175 Roo.MasterTemplate = function(){
13176     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13177     this.originalHtml = this.html;
13178     var st = {};
13179     var m, re = this.subTemplateRe;
13180     re.lastIndex = 0;
13181     var subIndex = 0;
13182     while(m = re.exec(this.html)){
13183         var name = m[1], content = m[2];
13184         st[subIndex] = {
13185             name: name,
13186             index: subIndex,
13187             buffer: [],
13188             tpl : new Roo.Template(content)
13189         };
13190         if(name){
13191             st[name] = st[subIndex];
13192         }
13193         st[subIndex].tpl.compile();
13194         st[subIndex].tpl.call = this.call.createDelegate(this);
13195         subIndex++;
13196     }
13197     this.subCount = subIndex;
13198     this.subs = st;
13199 };
13200 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13201     /**
13202     * The regular expression used to match sub templates
13203     * @type RegExp
13204     * @property
13205     */
13206     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13207
13208     /**
13209      * Applies the passed values to a child template.
13210      * @param {String/Number} name (optional) The name or index of the child template
13211      * @param {Array/Object} values The values to be applied to the template
13212      * @return {MasterTemplate} this
13213      */
13214      add : function(name, values){
13215         if(arguments.length == 1){
13216             values = arguments[0];
13217             name = 0;
13218         }
13219         var s = this.subs[name];
13220         s.buffer[s.buffer.length] = s.tpl.apply(values);
13221         return this;
13222     },
13223
13224     /**
13225      * Applies all the passed values to a child template.
13226      * @param {String/Number} name (optional) The name or index of the child template
13227      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13228      * @param {Boolean} reset (optional) True to reset the template first
13229      * @return {MasterTemplate} this
13230      */
13231     fill : function(name, values, reset){
13232         var a = arguments;
13233         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13234             values = a[0];
13235             name = 0;
13236             reset = a[1];
13237         }
13238         if(reset){
13239             this.reset();
13240         }
13241         for(var i = 0, len = values.length; i < len; i++){
13242             this.add(name, values[i]);
13243         }
13244         return this;
13245     },
13246
13247     /**
13248      * Resets the template for reuse
13249      * @return {MasterTemplate} this
13250      */
13251      reset : function(){
13252         var s = this.subs;
13253         for(var i = 0; i < this.subCount; i++){
13254             s[i].buffer = [];
13255         }
13256         return this;
13257     },
13258
13259     applyTemplate : function(values){
13260         var s = this.subs;
13261         var replaceIndex = -1;
13262         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13263             return s[++replaceIndex].buffer.join("");
13264         });
13265         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13266     },
13267
13268     apply : function(){
13269         return this.applyTemplate.apply(this, arguments);
13270     },
13271
13272     compile : function(){return this;}
13273 });
13274
13275 /**
13276  * Alias for fill().
13277  * @method
13278  */
13279 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13280  /**
13281  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13282  * var tpl = Roo.MasterTemplate.from('element-id');
13283  * @param {String/HTMLElement} el
13284  * @param {Object} config
13285  * @static
13286  */
13287 Roo.MasterTemplate.from = function(el, config){
13288     el = Roo.getDom(el);
13289     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13290 };/*
13291  * Based on:
13292  * Ext JS Library 1.1.1
13293  * Copyright(c) 2006-2007, Ext JS, LLC.
13294  *
13295  * Originally Released Under LGPL - original licence link has changed is not relivant.
13296  *
13297  * Fork - LGPL
13298  * <script type="text/javascript">
13299  */
13300
13301  
13302 /**
13303  * @class Roo.util.CSS
13304  * Utility class for manipulating CSS rules
13305  * @singleton
13306  */
13307 Roo.util.CSS = function(){
13308         var rules = null;
13309         var doc = document;
13310
13311     var camelRe = /(-[a-z])/gi;
13312     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13313
13314    return {
13315    /**
13316     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13317     * tag and appended to the HEAD of the document.
13318     * @param {String|Object} cssText The text containing the css rules
13319     * @param {String} id An id to add to the stylesheet for later removal
13320     * @return {StyleSheet}
13321     */
13322     createStyleSheet : function(cssText, id){
13323         var ss;
13324         var head = doc.getElementsByTagName("head")[0];
13325         var nrules = doc.createElement("style");
13326         nrules.setAttribute("type", "text/css");
13327         if(id){
13328             nrules.setAttribute("id", id);
13329         }
13330         if (typeof(cssText) != 'string') {
13331             // support object maps..
13332             // not sure if this a good idea.. 
13333             // perhaps it should be merged with the general css handling
13334             // and handle js style props.
13335             var cssTextNew = [];
13336             for(var n in cssText) {
13337                 var citems = [];
13338                 for(var k in cssText[n]) {
13339                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13340                 }
13341                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13342                 
13343             }
13344             cssText = cssTextNew.join("\n");
13345             
13346         }
13347        
13348        
13349        if(Roo.isIE){
13350            head.appendChild(nrules);
13351            ss = nrules.styleSheet;
13352            ss.cssText = cssText;
13353        }else{
13354            try{
13355                 nrules.appendChild(doc.createTextNode(cssText));
13356            }catch(e){
13357                nrules.cssText = cssText; 
13358            }
13359            head.appendChild(nrules);
13360            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13361        }
13362        this.cacheStyleSheet(ss);
13363        return ss;
13364    },
13365
13366    /**
13367     * Removes a style or link tag by id
13368     * @param {String} id The id of the tag
13369     */
13370    removeStyleSheet : function(id){
13371        var existing = doc.getElementById(id);
13372        if(existing){
13373            existing.parentNode.removeChild(existing);
13374        }
13375    },
13376
13377    /**
13378     * Dynamically swaps an existing stylesheet reference for a new one
13379     * @param {String} id The id of an existing link tag to remove
13380     * @param {String} url The href of the new stylesheet to include
13381     */
13382    swapStyleSheet : function(id, url){
13383        this.removeStyleSheet(id);
13384        var ss = doc.createElement("link");
13385        ss.setAttribute("rel", "stylesheet");
13386        ss.setAttribute("type", "text/css");
13387        ss.setAttribute("id", id);
13388        ss.setAttribute("href", url);
13389        doc.getElementsByTagName("head")[0].appendChild(ss);
13390    },
13391    
13392    /**
13393     * Refresh the rule cache if you have dynamically added stylesheets
13394     * @return {Object} An object (hash) of rules indexed by selector
13395     */
13396    refreshCache : function(){
13397        return this.getRules(true);
13398    },
13399
13400    // private
13401    cacheStyleSheet : function(stylesheet){
13402        if(!rules){
13403            rules = {};
13404        }
13405        try{// try catch for cross domain access issue
13406            var ssRules = stylesheet.cssRules || stylesheet.rules;
13407            for(var j = ssRules.length-1; j >= 0; --j){
13408                rules[ssRules[j].selectorText] = ssRules[j];
13409            }
13410        }catch(e){}
13411    },
13412    
13413    /**
13414     * Gets all css rules for the document
13415     * @param {Boolean} refreshCache true to refresh the internal cache
13416     * @return {Object} An object (hash) of rules indexed by selector
13417     */
13418    getRules : function(refreshCache){
13419                 if(rules == null || refreshCache){
13420                         rules = {};
13421                         var ds = doc.styleSheets;
13422                         for(var i =0, len = ds.length; i < len; i++){
13423                             try{
13424                         this.cacheStyleSheet(ds[i]);
13425                     }catch(e){} 
13426                 }
13427                 }
13428                 return rules;
13429         },
13430         
13431         /**
13432     * Gets an an individual CSS rule by selector(s)
13433     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13434     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13435     * @return {CSSRule} The CSS rule or null if one is not found
13436     */
13437    getRule : function(selector, refreshCache){
13438                 var rs = this.getRules(refreshCache);
13439                 if(!(selector instanceof Array)){
13440                     return rs[selector];
13441                 }
13442                 for(var i = 0; i < selector.length; i++){
13443                         if(rs[selector[i]]){
13444                                 return rs[selector[i]];
13445                         }
13446                 }
13447                 return null;
13448         },
13449         
13450         
13451         /**
13452     * Updates a rule property
13453     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13454     * @param {String} property The css property
13455     * @param {String} value The new value for the property
13456     * @return {Boolean} true If a rule was found and updated
13457     */
13458    updateRule : function(selector, property, value){
13459                 if(!(selector instanceof Array)){
13460                         var rule = this.getRule(selector);
13461                         if(rule){
13462                                 rule.style[property.replace(camelRe, camelFn)] = value;
13463                                 return true;
13464                         }
13465                 }else{
13466                         for(var i = 0; i < selector.length; i++){
13467                                 if(this.updateRule(selector[i], property, value)){
13468                                         return true;
13469                                 }
13470                         }
13471                 }
13472                 return false;
13473         }
13474    };   
13475 }();/*
13476  * Based on:
13477  * Ext JS Library 1.1.1
13478  * Copyright(c) 2006-2007, Ext JS, LLC.
13479  *
13480  * Originally Released Under LGPL - original licence link has changed is not relivant.
13481  *
13482  * Fork - LGPL
13483  * <script type="text/javascript">
13484  */
13485
13486  
13487
13488 /**
13489  * @class Roo.util.ClickRepeater
13490  * @extends Roo.util.Observable
13491  * 
13492  * A wrapper class which can be applied to any element. Fires a "click" event while the
13493  * mouse is pressed. The interval between firings may be specified in the config but
13494  * defaults to 10 milliseconds.
13495  * 
13496  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13497  * 
13498  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13499  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13500  * Similar to an autorepeat key delay.
13501  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13502  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13503  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13504  *           "interval" and "delay" are ignored. "immediate" is honored.
13505  * @cfg {Boolean} preventDefault True to prevent the default click event
13506  * @cfg {Boolean} stopDefault True to stop the default click event
13507  * 
13508  * @history
13509  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13510  *     2007-02-02 jvs Renamed to ClickRepeater
13511  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13512  *
13513  *  @constructor
13514  * @param {String/HTMLElement/Element} el The element to listen on
13515  * @param {Object} config
13516  **/
13517 Roo.util.ClickRepeater = function(el, config)
13518 {
13519     this.el = Roo.get(el);
13520     this.el.unselectable();
13521
13522     Roo.apply(this, config);
13523
13524     this.addEvents({
13525     /**
13526      * @event mousedown
13527      * Fires when the mouse button is depressed.
13528      * @param {Roo.util.ClickRepeater} this
13529      */
13530         "mousedown" : true,
13531     /**
13532      * @event click
13533      * Fires on a specified interval during the time the element is pressed.
13534      * @param {Roo.util.ClickRepeater} this
13535      */
13536         "click" : true,
13537     /**
13538      * @event mouseup
13539      * Fires when the mouse key is released.
13540      * @param {Roo.util.ClickRepeater} this
13541      */
13542         "mouseup" : true
13543     });
13544
13545     this.el.on("mousedown", this.handleMouseDown, this);
13546     if(this.preventDefault || this.stopDefault){
13547         this.el.on("click", function(e){
13548             if(this.preventDefault){
13549                 e.preventDefault();
13550             }
13551             if(this.stopDefault){
13552                 e.stopEvent();
13553             }
13554         }, this);
13555     }
13556
13557     // allow inline handler
13558     if(this.handler){
13559         this.on("click", this.handler,  this.scope || this);
13560     }
13561
13562     Roo.util.ClickRepeater.superclass.constructor.call(this);
13563 };
13564
13565 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13566     interval : 20,
13567     delay: 250,
13568     preventDefault : true,
13569     stopDefault : false,
13570     timer : 0,
13571
13572     // private
13573     handleMouseDown : function(){
13574         clearTimeout(this.timer);
13575         this.el.blur();
13576         if(this.pressClass){
13577             this.el.addClass(this.pressClass);
13578         }
13579         this.mousedownTime = new Date();
13580
13581         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13582         this.el.on("mouseout", this.handleMouseOut, this);
13583
13584         this.fireEvent("mousedown", this);
13585         this.fireEvent("click", this);
13586         
13587         this.timer = this.click.defer(this.delay || this.interval, this);
13588     },
13589
13590     // private
13591     click : function(){
13592         this.fireEvent("click", this);
13593         this.timer = this.click.defer(this.getInterval(), this);
13594     },
13595
13596     // private
13597     getInterval: function(){
13598         if(!this.accelerate){
13599             return this.interval;
13600         }
13601         var pressTime = this.mousedownTime.getElapsed();
13602         if(pressTime < 500){
13603             return 400;
13604         }else if(pressTime < 1700){
13605             return 320;
13606         }else if(pressTime < 2600){
13607             return 250;
13608         }else if(pressTime < 3500){
13609             return 180;
13610         }else if(pressTime < 4400){
13611             return 140;
13612         }else if(pressTime < 5300){
13613             return 80;
13614         }else if(pressTime < 6200){
13615             return 50;
13616         }else{
13617             return 10;
13618         }
13619     },
13620
13621     // private
13622     handleMouseOut : function(){
13623         clearTimeout(this.timer);
13624         if(this.pressClass){
13625             this.el.removeClass(this.pressClass);
13626         }
13627         this.el.on("mouseover", this.handleMouseReturn, this);
13628     },
13629
13630     // private
13631     handleMouseReturn : function(){
13632         this.el.un("mouseover", this.handleMouseReturn);
13633         if(this.pressClass){
13634             this.el.addClass(this.pressClass);
13635         }
13636         this.click();
13637     },
13638
13639     // private
13640     handleMouseUp : function(){
13641         clearTimeout(this.timer);
13642         this.el.un("mouseover", this.handleMouseReturn);
13643         this.el.un("mouseout", this.handleMouseOut);
13644         Roo.get(document).un("mouseup", this.handleMouseUp);
13645         this.el.removeClass(this.pressClass);
13646         this.fireEvent("mouseup", this);
13647     }
13648 });/*
13649  * Based on:
13650  * Ext JS Library 1.1.1
13651  * Copyright(c) 2006-2007, Ext JS, LLC.
13652  *
13653  * Originally Released Under LGPL - original licence link has changed is not relivant.
13654  *
13655  * Fork - LGPL
13656  * <script type="text/javascript">
13657  */
13658
13659  
13660 /**
13661  * @class Roo.KeyNav
13662  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13663  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13664  * way to implement custom navigation schemes for any UI component.</p>
13665  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13666  * pageUp, pageDown, del, home, end.  Usage:</p>
13667  <pre><code>
13668 var nav = new Roo.KeyNav("my-element", {
13669     "left" : function(e){
13670         this.moveLeft(e.ctrlKey);
13671     },
13672     "right" : function(e){
13673         this.moveRight(e.ctrlKey);
13674     },
13675     "enter" : function(e){
13676         this.save();
13677     },
13678     scope : this
13679 });
13680 </code></pre>
13681  * @constructor
13682  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13683  * @param {Object} config The config
13684  */
13685 Roo.KeyNav = function(el, config){
13686     this.el = Roo.get(el);
13687     Roo.apply(this, config);
13688     if(!this.disabled){
13689         this.disabled = true;
13690         this.enable();
13691     }
13692 };
13693
13694 Roo.KeyNav.prototype = {
13695     /**
13696      * @cfg {Boolean} disabled
13697      * True to disable this KeyNav instance (defaults to false)
13698      */
13699     disabled : false,
13700     /**
13701      * @cfg {String} defaultEventAction
13702      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13703      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13704      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13705      */
13706     defaultEventAction: "stopEvent",
13707     /**
13708      * @cfg {Boolean} forceKeyDown
13709      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13710      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13711      * handle keydown instead of keypress.
13712      */
13713     forceKeyDown : false,
13714
13715     // private
13716     prepareEvent : function(e){
13717         var k = e.getKey();
13718         var h = this.keyToHandler[k];
13719         //if(h && this[h]){
13720         //    e.stopPropagation();
13721         //}
13722         if(Roo.isSafari && h && k >= 37 && k <= 40){
13723             e.stopEvent();
13724         }
13725     },
13726
13727     // private
13728     relay : function(e){
13729         var k = e.getKey();
13730         var h = this.keyToHandler[k];
13731         if(h && this[h]){
13732             if(this.doRelay(e, this[h], h) !== true){
13733                 e[this.defaultEventAction]();
13734             }
13735         }
13736     },
13737
13738     // private
13739     doRelay : function(e, h, hname){
13740         return h.call(this.scope || this, e);
13741     },
13742
13743     // possible handlers
13744     enter : false,
13745     left : false,
13746     right : false,
13747     up : false,
13748     down : false,
13749     tab : false,
13750     esc : false,
13751     pageUp : false,
13752     pageDown : false,
13753     del : false,
13754     home : false,
13755     end : false,
13756
13757     // quick lookup hash
13758     keyToHandler : {
13759         37 : "left",
13760         39 : "right",
13761         38 : "up",
13762         40 : "down",
13763         33 : "pageUp",
13764         34 : "pageDown",
13765         46 : "del",
13766         36 : "home",
13767         35 : "end",
13768         13 : "enter",
13769         27 : "esc",
13770         9  : "tab"
13771     },
13772
13773         /**
13774          * Enable this KeyNav
13775          */
13776         enable: function(){
13777                 if(this.disabled){
13778             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13779             // the EventObject will normalize Safari automatically
13780             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13781                 this.el.on("keydown", this.relay,  this);
13782             }else{
13783                 this.el.on("keydown", this.prepareEvent,  this);
13784                 this.el.on("keypress", this.relay,  this);
13785             }
13786                     this.disabled = false;
13787                 }
13788         },
13789
13790         /**
13791          * Disable this KeyNav
13792          */
13793         disable: function(){
13794                 if(!this.disabled){
13795                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13796                 this.el.un("keydown", this.relay);
13797             }else{
13798                 this.el.un("keydown", this.prepareEvent);
13799                 this.el.un("keypress", this.relay);
13800             }
13801                     this.disabled = true;
13802                 }
13803         }
13804 };/*
13805  * Based on:
13806  * Ext JS Library 1.1.1
13807  * Copyright(c) 2006-2007, Ext JS, LLC.
13808  *
13809  * Originally Released Under LGPL - original licence link has changed is not relivant.
13810  *
13811  * Fork - LGPL
13812  * <script type="text/javascript">
13813  */
13814
13815  
13816 /**
13817  * @class Roo.KeyMap
13818  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13819  * The constructor accepts the same config object as defined by {@link #addBinding}.
13820  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13821  * combination it will call the function with this signature (if the match is a multi-key
13822  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13823  * A KeyMap can also handle a string representation of keys.<br />
13824  * Usage:
13825  <pre><code>
13826 // map one key by key code
13827 var map = new Roo.KeyMap("my-element", {
13828     key: 13, // or Roo.EventObject.ENTER
13829     fn: myHandler,
13830     scope: myObject
13831 });
13832
13833 // map multiple keys to one action by string
13834 var map = new Roo.KeyMap("my-element", {
13835     key: "a\r\n\t",
13836     fn: myHandler,
13837     scope: myObject
13838 });
13839
13840 // map multiple keys to multiple actions by strings and array of codes
13841 var map = new Roo.KeyMap("my-element", [
13842     {
13843         key: [10,13],
13844         fn: function(){ alert("Return was pressed"); }
13845     }, {
13846         key: "abc",
13847         fn: function(){ alert('a, b or c was pressed'); }
13848     }, {
13849         key: "\t",
13850         ctrl:true,
13851         shift:true,
13852         fn: function(){ alert('Control + shift + tab was pressed.'); }
13853     }
13854 ]);
13855 </code></pre>
13856  * <b>Note: A KeyMap starts enabled</b>
13857  * @constructor
13858  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13859  * @param {Object} config The config (see {@link #addBinding})
13860  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13861  */
13862 Roo.KeyMap = function(el, config, eventName){
13863     this.el  = Roo.get(el);
13864     this.eventName = eventName || "keydown";
13865     this.bindings = [];
13866     if(config){
13867         this.addBinding(config);
13868     }
13869     this.enable();
13870 };
13871
13872 Roo.KeyMap.prototype = {
13873     /**
13874      * True to stop the event from bubbling and prevent the default browser action if the
13875      * key was handled by the KeyMap (defaults to false)
13876      * @type Boolean
13877      */
13878     stopEvent : false,
13879
13880     /**
13881      * Add a new binding to this KeyMap. The following config object properties are supported:
13882      * <pre>
13883 Property    Type             Description
13884 ----------  ---------------  ----------------------------------------------------------------------
13885 key         String/Array     A single keycode or an array of keycodes to handle
13886 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13887 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13888 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13889 fn          Function         The function to call when KeyMap finds the expected key combination
13890 scope       Object           The scope of the callback function
13891 </pre>
13892      *
13893      * Usage:
13894      * <pre><code>
13895 // Create a KeyMap
13896 var map = new Roo.KeyMap(document, {
13897     key: Roo.EventObject.ENTER,
13898     fn: handleKey,
13899     scope: this
13900 });
13901
13902 //Add a new binding to the existing KeyMap later
13903 map.addBinding({
13904     key: 'abc',
13905     shift: true,
13906     fn: handleKey,
13907     scope: this
13908 });
13909 </code></pre>
13910      * @param {Object/Array} config A single KeyMap config or an array of configs
13911      */
13912         addBinding : function(config){
13913         if(config instanceof Array){
13914             for(var i = 0, len = config.length; i < len; i++){
13915                 this.addBinding(config[i]);
13916             }
13917             return;
13918         }
13919         var keyCode = config.key,
13920             shift = config.shift, 
13921             ctrl = config.ctrl, 
13922             alt = config.alt,
13923             fn = config.fn,
13924             scope = config.scope;
13925         if(typeof keyCode == "string"){
13926             var ks = [];
13927             var keyString = keyCode.toUpperCase();
13928             for(var j = 0, len = keyString.length; j < len; j++){
13929                 ks.push(keyString.charCodeAt(j));
13930             }
13931             keyCode = ks;
13932         }
13933         var keyArray = keyCode instanceof Array;
13934         var handler = function(e){
13935             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13936                 var k = e.getKey();
13937                 if(keyArray){
13938                     for(var i = 0, len = keyCode.length; i < len; i++){
13939                         if(keyCode[i] == k){
13940                           if(this.stopEvent){
13941                               e.stopEvent();
13942                           }
13943                           fn.call(scope || window, k, e);
13944                           return;
13945                         }
13946                     }
13947                 }else{
13948                     if(k == keyCode){
13949                         if(this.stopEvent){
13950                            e.stopEvent();
13951                         }
13952                         fn.call(scope || window, k, e);
13953                     }
13954                 }
13955             }
13956         };
13957         this.bindings.push(handler);  
13958         },
13959
13960     /**
13961      * Shorthand for adding a single key listener
13962      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13963      * following options:
13964      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13965      * @param {Function} fn The function to call
13966      * @param {Object} scope (optional) The scope of the function
13967      */
13968     on : function(key, fn, scope){
13969         var keyCode, shift, ctrl, alt;
13970         if(typeof key == "object" && !(key instanceof Array)){
13971             keyCode = key.key;
13972             shift = key.shift;
13973             ctrl = key.ctrl;
13974             alt = key.alt;
13975         }else{
13976             keyCode = key;
13977         }
13978         this.addBinding({
13979             key: keyCode,
13980             shift: shift,
13981             ctrl: ctrl,
13982             alt: alt,
13983             fn: fn,
13984             scope: scope
13985         })
13986     },
13987
13988     // private
13989     handleKeyDown : function(e){
13990             if(this.enabled){ //just in case
13991             var b = this.bindings;
13992             for(var i = 0, len = b.length; i < len; i++){
13993                 b[i].call(this, e);
13994             }
13995             }
13996         },
13997         
13998         /**
13999          * Returns true if this KeyMap is enabled
14000          * @return {Boolean} 
14001          */
14002         isEnabled : function(){
14003             return this.enabled;  
14004         },
14005         
14006         /**
14007          * Enables this KeyMap
14008          */
14009         enable: function(){
14010                 if(!this.enabled){
14011                     this.el.on(this.eventName, this.handleKeyDown, this);
14012                     this.enabled = true;
14013                 }
14014         },
14015
14016         /**
14017          * Disable this KeyMap
14018          */
14019         disable: function(){
14020                 if(this.enabled){
14021                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14022                     this.enabled = false;
14023                 }
14024         }
14025 };/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037 /**
14038  * @class Roo.util.TextMetrics
14039  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14040  * wide, in pixels, a given block of text will be.
14041  * @singleton
14042  */
14043 Roo.util.TextMetrics = function(){
14044     var shared;
14045     return {
14046         /**
14047          * Measures the size of the specified text
14048          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14049          * that can affect the size of the rendered text
14050          * @param {String} text The text to measure
14051          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14052          * in order to accurately measure the text height
14053          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14054          */
14055         measure : function(el, text, fixedWidth){
14056             if(!shared){
14057                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14058             }
14059             shared.bind(el);
14060             shared.setFixedWidth(fixedWidth || 'auto');
14061             return shared.getSize(text);
14062         },
14063
14064         /**
14065          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14066          * the overhead of multiple calls to initialize the style properties on each measurement.
14067          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14068          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14069          * in order to accurately measure the text height
14070          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14071          */
14072         createInstance : function(el, fixedWidth){
14073             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14074         }
14075     };
14076 }();
14077
14078  
14079
14080 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14081     var ml = new Roo.Element(document.createElement('div'));
14082     document.body.appendChild(ml.dom);
14083     ml.position('absolute');
14084     ml.setLeftTop(-1000, -1000);
14085     ml.hide();
14086
14087     if(fixedWidth){
14088         ml.setWidth(fixedWidth);
14089     }
14090      
14091     var instance = {
14092         /**
14093          * Returns the size of the specified text based on the internal element's style and width properties
14094          * @memberOf Roo.util.TextMetrics.Instance#
14095          * @param {String} text The text to measure
14096          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14097          */
14098         getSize : function(text){
14099             ml.update(text);
14100             var s = ml.getSize();
14101             ml.update('');
14102             return s;
14103         },
14104
14105         /**
14106          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14107          * that can affect the size of the rendered text
14108          * @memberOf Roo.util.TextMetrics.Instance#
14109          * @param {String/HTMLElement} el The element, dom node or id
14110          */
14111         bind : function(el){
14112             ml.setStyle(
14113                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14114             );
14115         },
14116
14117         /**
14118          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14119          * to set a fixed width in order to accurately measure the text height.
14120          * @memberOf Roo.util.TextMetrics.Instance#
14121          * @param {Number} width The width to set on the element
14122          */
14123         setFixedWidth : function(width){
14124             ml.setWidth(width);
14125         },
14126
14127         /**
14128          * Returns the measured width of the specified text
14129          * @memberOf Roo.util.TextMetrics.Instance#
14130          * @param {String} text The text to measure
14131          * @return {Number} width The width in pixels
14132          */
14133         getWidth : function(text){
14134             ml.dom.style.width = 'auto';
14135             return this.getSize(text).width;
14136         },
14137
14138         /**
14139          * Returns the measured height of the specified text.  For multiline text, be sure to call
14140          * {@link #setFixedWidth} if necessary.
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String} text The text to measure
14143          * @return {Number} height The height in pixels
14144          */
14145         getHeight : function(text){
14146             return this.getSize(text).height;
14147         }
14148     };
14149
14150     instance.bind(bindTo);
14151
14152     return instance;
14153 };
14154
14155 // backwards compat
14156 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14157  * Based on:
14158  * Ext JS Library 1.1.1
14159  * Copyright(c) 2006-2007, Ext JS, LLC.
14160  *
14161  * Originally Released Under LGPL - original licence link has changed is not relivant.
14162  *
14163  * Fork - LGPL
14164  * <script type="text/javascript">
14165  */
14166
14167 /**
14168  * @class Roo.state.Provider
14169  * Abstract base class for state provider implementations. This class provides methods
14170  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14171  * Provider interface.
14172  */
14173 Roo.state.Provider = function(){
14174     /**
14175      * @event statechange
14176      * Fires when a state change occurs.
14177      * @param {Provider} this This state provider
14178      * @param {String} key The state key which was changed
14179      * @param {String} value The encoded value for the state
14180      */
14181     this.addEvents({
14182         "statechange": true
14183     });
14184     this.state = {};
14185     Roo.state.Provider.superclass.constructor.call(this);
14186 };
14187 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14188     /**
14189      * Returns the current value for a key
14190      * @param {String} name The key name
14191      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14192      * @return {Mixed} The state data
14193      */
14194     get : function(name, defaultValue){
14195         return typeof this.state[name] == "undefined" ?
14196             defaultValue : this.state[name];
14197     },
14198     
14199     /**
14200      * Clears a value from the state
14201      * @param {String} name The key name
14202      */
14203     clear : function(name){
14204         delete this.state[name];
14205         this.fireEvent("statechange", this, name, null);
14206     },
14207     
14208     /**
14209      * Sets the value for a key
14210      * @param {String} name The key name
14211      * @param {Mixed} value The value to set
14212      */
14213     set : function(name, value){
14214         this.state[name] = value;
14215         this.fireEvent("statechange", this, name, value);
14216     },
14217     
14218     /**
14219      * Decodes a string previously encoded with {@link #encodeValue}.
14220      * @param {String} value The value to decode
14221      * @return {Mixed} The decoded value
14222      */
14223     decodeValue : function(cookie){
14224         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14225         var matches = re.exec(unescape(cookie));
14226         if(!matches || !matches[1]) return; // non state cookie
14227         var type = matches[1];
14228         var v = matches[2];
14229         switch(type){
14230             case "n":
14231                 return parseFloat(v);
14232             case "d":
14233                 return new Date(Date.parse(v));
14234             case "b":
14235                 return (v == "1");
14236             case "a":
14237                 var all = [];
14238                 var values = v.split("^");
14239                 for(var i = 0, len = values.length; i < len; i++){
14240                     all.push(this.decodeValue(values[i]));
14241                 }
14242                 return all;
14243            case "o":
14244                 var all = {};
14245                 var values = v.split("^");
14246                 for(var i = 0, len = values.length; i < len; i++){
14247                     var kv = values[i].split("=");
14248                     all[kv[0]] = this.decodeValue(kv[1]);
14249                 }
14250                 return all;
14251            default:
14252                 return v;
14253         }
14254     },
14255     
14256     /**
14257      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14258      * @param {Mixed} value The value to encode
14259      * @return {String} The encoded value
14260      */
14261     encodeValue : function(v){
14262         var enc;
14263         if(typeof v == "number"){
14264             enc = "n:" + v;
14265         }else if(typeof v == "boolean"){
14266             enc = "b:" + (v ? "1" : "0");
14267         }else if(v instanceof Date){
14268             enc = "d:" + v.toGMTString();
14269         }else if(v instanceof Array){
14270             var flat = "";
14271             for(var i = 0, len = v.length; i < len; i++){
14272                 flat += this.encodeValue(v[i]);
14273                 if(i != len-1) flat += "^";
14274             }
14275             enc = "a:" + flat;
14276         }else if(typeof v == "object"){
14277             var flat = "";
14278             for(var key in v){
14279                 if(typeof v[key] != "function"){
14280                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14281                 }
14282             }
14283             enc = "o:" + flat.substring(0, flat.length-1);
14284         }else{
14285             enc = "s:" + v;
14286         }
14287         return escape(enc);        
14288     }
14289 });
14290
14291 /*
14292  * Based on:
14293  * Ext JS Library 1.1.1
14294  * Copyright(c) 2006-2007, Ext JS, LLC.
14295  *
14296  * Originally Released Under LGPL - original licence link has changed is not relivant.
14297  *
14298  * Fork - LGPL
14299  * <script type="text/javascript">
14300  */
14301 /**
14302  * @class Roo.state.Manager
14303  * This is the global state manager. By default all components that are "state aware" check this class
14304  * for state information if you don't pass them a custom state provider. In order for this class
14305  * to be useful, it must be initialized with a provider when your application initializes.
14306  <pre><code>
14307 // in your initialization function
14308 init : function(){
14309    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14310    ...
14311    // supposed you have a {@link Roo.BorderLayout}
14312    var layout = new Roo.BorderLayout(...);
14313    layout.restoreState();
14314    // or a {Roo.BasicDialog}
14315    var dialog = new Roo.BasicDialog(...);
14316    dialog.restoreState();
14317  </code></pre>
14318  * @singleton
14319  */
14320 Roo.state.Manager = function(){
14321     var provider = new Roo.state.Provider();
14322     
14323     return {
14324         /**
14325          * Configures the default state provider for your application
14326          * @param {Provider} stateProvider The state provider to set
14327          */
14328         setProvider : function(stateProvider){
14329             provider = stateProvider;
14330         },
14331         
14332         /**
14333          * Returns the current value for a key
14334          * @param {String} name The key name
14335          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14336          * @return {Mixed} The state data
14337          */
14338         get : function(key, defaultValue){
14339             return provider.get(key, defaultValue);
14340         },
14341         
14342         /**
14343          * Sets the value for a key
14344          * @param {String} name The key name
14345          * @param {Mixed} value The state data
14346          */
14347          set : function(key, value){
14348             provider.set(key, value);
14349         },
14350         
14351         /**
14352          * Clears a value from the state
14353          * @param {String} name The key name
14354          */
14355         clear : function(key){
14356             provider.clear(key);
14357         },
14358         
14359         /**
14360          * Gets the currently configured state provider
14361          * @return {Provider} The state provider
14362          */
14363         getProvider : function(){
14364             return provider;
14365         }
14366     };
14367 }();
14368 /*
14369  * Based on:
14370  * Ext JS Library 1.1.1
14371  * Copyright(c) 2006-2007, Ext JS, LLC.
14372  *
14373  * Originally Released Under LGPL - original licence link has changed is not relivant.
14374  *
14375  * Fork - LGPL
14376  * <script type="text/javascript">
14377  */
14378 /**
14379  * @class Roo.state.CookieProvider
14380  * @extends Roo.state.Provider
14381  * The default Provider implementation which saves state via cookies.
14382  * <br />Usage:
14383  <pre><code>
14384    var cp = new Roo.state.CookieProvider({
14385        path: "/cgi-bin/",
14386        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14387        domain: "roojs.com"
14388    })
14389    Roo.state.Manager.setProvider(cp);
14390  </code></pre>
14391  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14392  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14393  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14394  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14395  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14396  * domain the page is running on including the 'www' like 'www.roojs.com')
14397  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14398  * @constructor
14399  * Create a new CookieProvider
14400  * @param {Object} config The configuration object
14401  */
14402 Roo.state.CookieProvider = function(config){
14403     Roo.state.CookieProvider.superclass.constructor.call(this);
14404     this.path = "/";
14405     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14406     this.domain = null;
14407     this.secure = false;
14408     Roo.apply(this, config);
14409     this.state = this.readCookies();
14410 };
14411
14412 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14413     // private
14414     set : function(name, value){
14415         if(typeof value == "undefined" || value === null){
14416             this.clear(name);
14417             return;
14418         }
14419         this.setCookie(name, value);
14420         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14421     },
14422
14423     // private
14424     clear : function(name){
14425         this.clearCookie(name);
14426         Roo.state.CookieProvider.superclass.clear.call(this, name);
14427     },
14428
14429     // private
14430     readCookies : function(){
14431         var cookies = {};
14432         var c = document.cookie + ";";
14433         var re = /\s?(.*?)=(.*?);/g;
14434         var matches;
14435         while((matches = re.exec(c)) != null){
14436             var name = matches[1];
14437             var value = matches[2];
14438             if(name && name.substring(0,3) == "ys-"){
14439                 cookies[name.substr(3)] = this.decodeValue(value);
14440             }
14441         }
14442         return cookies;
14443     },
14444
14445     // private
14446     setCookie : function(name, value){
14447         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14448            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14449            ((this.path == null) ? "" : ("; path=" + this.path)) +
14450            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14451            ((this.secure == true) ? "; secure" : "");
14452     },
14453
14454     // private
14455     clearCookie : function(name){
14456         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14457            ((this.path == null) ? "" : ("; path=" + this.path)) +
14458            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14459            ((this.secure == true) ? "; secure" : "");
14460     }
14461 });/*
14462  * Based on:
14463  * Ext JS Library 1.1.1
14464  * Copyright(c) 2006-2007, Ext JS, LLC.
14465  *
14466  * Originally Released Under LGPL - original licence link has changed is not relivant.
14467  *
14468  * Fork - LGPL
14469  * <script type="text/javascript">
14470  */
14471
14472
14473
14474 /*
14475  * These classes are derivatives of the similarly named classes in the YUI Library.
14476  * The original license:
14477  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14478  * Code licensed under the BSD License:
14479  * http://developer.yahoo.net/yui/license.txt
14480  */
14481
14482 (function() {
14483
14484 var Event=Roo.EventManager;
14485 var Dom=Roo.lib.Dom;
14486
14487 /**
14488  * @class Roo.dd.DragDrop
14489  * @extends Roo.util.Observable
14490  * Defines the interface and base operation of items that that can be
14491  * dragged or can be drop targets.  It was designed to be extended, overriding
14492  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14493  * Up to three html elements can be associated with a DragDrop instance:
14494  * <ul>
14495  * <li>linked element: the element that is passed into the constructor.
14496  * This is the element which defines the boundaries for interaction with
14497  * other DragDrop objects.</li>
14498  * <li>handle element(s): The drag operation only occurs if the element that
14499  * was clicked matches a handle element.  By default this is the linked
14500  * element, but there are times that you will want only a portion of the
14501  * linked element to initiate the drag operation, and the setHandleElId()
14502  * method provides a way to define this.</li>
14503  * <li>drag element: this represents the element that would be moved along
14504  * with the cursor during a drag operation.  By default, this is the linked
14505  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14506  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14507  * </li>
14508  * </ul>
14509  * This class should not be instantiated until the onload event to ensure that
14510  * the associated elements are available.
14511  * The following would define a DragDrop obj that would interact with any
14512  * other DragDrop obj in the "group1" group:
14513  * <pre>
14514  *  dd = new Roo.dd.DragDrop("div1", "group1");
14515  * </pre>
14516  * Since none of the event handlers have been implemented, nothing would
14517  * actually happen if you were to run the code above.  Normally you would
14518  * override this class or one of the default implementations, but you can
14519  * also override the methods you want on an instance of the class...
14520  * <pre>
14521  *  dd.onDragDrop = function(e, id) {
14522  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14523  *  }
14524  * </pre>
14525  * @constructor
14526  * @param {String} id of the element that is linked to this instance
14527  * @param {String} sGroup the group of related DragDrop objects
14528  * @param {object} config an object containing configurable attributes
14529  *                Valid properties for DragDrop:
14530  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14531  */
14532 Roo.dd.DragDrop = function(id, sGroup, config) {
14533     if (id) {
14534         this.init(id, sGroup, config);
14535     }
14536     
14537 };
14538
14539 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14540
14541     /**
14542      * The id of the element associated with this object.  This is what we
14543      * refer to as the "linked element" because the size and position of
14544      * this element is used to determine when the drag and drop objects have
14545      * interacted.
14546      * @property id
14547      * @type String
14548      */
14549     id: null,
14550
14551     /**
14552      * Configuration attributes passed into the constructor
14553      * @property config
14554      * @type object
14555      */
14556     config: null,
14557
14558     /**
14559      * The id of the element that will be dragged.  By default this is same
14560      * as the linked element , but could be changed to another element. Ex:
14561      * Roo.dd.DDProxy
14562      * @property dragElId
14563      * @type String
14564      * @private
14565      */
14566     dragElId: null,
14567
14568     /**
14569      * the id of the element that initiates the drag operation.  By default
14570      * this is the linked element, but could be changed to be a child of this
14571      * element.  This lets us do things like only starting the drag when the
14572      * header element within the linked html element is clicked.
14573      * @property handleElId
14574      * @type String
14575      * @private
14576      */
14577     handleElId: null,
14578
14579     /**
14580      * An associative array of HTML tags that will be ignored if clicked.
14581      * @property invalidHandleTypes
14582      * @type {string: string}
14583      */
14584     invalidHandleTypes: null,
14585
14586     /**
14587      * An associative array of ids for elements that will be ignored if clicked
14588      * @property invalidHandleIds
14589      * @type {string: string}
14590      */
14591     invalidHandleIds: null,
14592
14593     /**
14594      * An indexted array of css class names for elements that will be ignored
14595      * if clicked.
14596      * @property invalidHandleClasses
14597      * @type string[]
14598      */
14599     invalidHandleClasses: null,
14600
14601     /**
14602      * The linked element's absolute X position at the time the drag was
14603      * started
14604      * @property startPageX
14605      * @type int
14606      * @private
14607      */
14608     startPageX: 0,
14609
14610     /**
14611      * The linked element's absolute X position at the time the drag was
14612      * started
14613      * @property startPageY
14614      * @type int
14615      * @private
14616      */
14617     startPageY: 0,
14618
14619     /**
14620      * The group defines a logical collection of DragDrop objects that are
14621      * related.  Instances only get events when interacting with other
14622      * DragDrop object in the same group.  This lets us define multiple
14623      * groups using a single DragDrop subclass if we want.
14624      * @property groups
14625      * @type {string: string}
14626      */
14627     groups: null,
14628
14629     /**
14630      * Individual drag/drop instances can be locked.  This will prevent
14631      * onmousedown start drag.
14632      * @property locked
14633      * @type boolean
14634      * @private
14635      */
14636     locked: false,
14637
14638     /**
14639      * Lock this instance
14640      * @method lock
14641      */
14642     lock: function() { this.locked = true; },
14643
14644     /**
14645      * Unlock this instace
14646      * @method unlock
14647      */
14648     unlock: function() { this.locked = false; },
14649
14650     /**
14651      * By default, all insances can be a drop target.  This can be disabled by
14652      * setting isTarget to false.
14653      * @method isTarget
14654      * @type boolean
14655      */
14656     isTarget: true,
14657
14658     /**
14659      * The padding configured for this drag and drop object for calculating
14660      * the drop zone intersection with this object.
14661      * @method padding
14662      * @type int[]
14663      */
14664     padding: null,
14665
14666     /**
14667      * Cached reference to the linked element
14668      * @property _domRef
14669      * @private
14670      */
14671     _domRef: null,
14672
14673     /**
14674      * Internal typeof flag
14675      * @property __ygDragDrop
14676      * @private
14677      */
14678     __ygDragDrop: true,
14679
14680     /**
14681      * Set to true when horizontal contraints are applied
14682      * @property constrainX
14683      * @type boolean
14684      * @private
14685      */
14686     constrainX: false,
14687
14688     /**
14689      * Set to true when vertical contraints are applied
14690      * @property constrainY
14691      * @type boolean
14692      * @private
14693      */
14694     constrainY: false,
14695
14696     /**
14697      * The left constraint
14698      * @property minX
14699      * @type int
14700      * @private
14701      */
14702     minX: 0,
14703
14704     /**
14705      * The right constraint
14706      * @property maxX
14707      * @type int
14708      * @private
14709      */
14710     maxX: 0,
14711
14712     /**
14713      * The up constraint
14714      * @property minY
14715      * @type int
14716      * @type int
14717      * @private
14718      */
14719     minY: 0,
14720
14721     /**
14722      * The down constraint
14723      * @property maxY
14724      * @type int
14725      * @private
14726      */
14727     maxY: 0,
14728
14729     /**
14730      * Maintain offsets when we resetconstraints.  Set to true when you want
14731      * the position of the element relative to its parent to stay the same
14732      * when the page changes
14733      *
14734      * @property maintainOffset
14735      * @type boolean
14736      */
14737     maintainOffset: false,
14738
14739     /**
14740      * Array of pixel locations the element will snap to if we specified a
14741      * horizontal graduation/interval.  This array is generated automatically
14742      * when you define a tick interval.
14743      * @property xTicks
14744      * @type int[]
14745      */
14746     xTicks: null,
14747
14748     /**
14749      * Array of pixel locations the element will snap to if we specified a
14750      * vertical graduation/interval.  This array is generated automatically
14751      * when you define a tick interval.
14752      * @property yTicks
14753      * @type int[]
14754      */
14755     yTicks: null,
14756
14757     /**
14758      * By default the drag and drop instance will only respond to the primary
14759      * button click (left button for a right-handed mouse).  Set to true to
14760      * allow drag and drop to start with any mouse click that is propogated
14761      * by the browser
14762      * @property primaryButtonOnly
14763      * @type boolean
14764      */
14765     primaryButtonOnly: true,
14766
14767     /**
14768      * The availabe property is false until the linked dom element is accessible.
14769      * @property available
14770      * @type boolean
14771      */
14772     available: false,
14773
14774     /**
14775      * By default, drags can only be initiated if the mousedown occurs in the
14776      * region the linked element is.  This is done in part to work around a
14777      * bug in some browsers that mis-report the mousedown if the previous
14778      * mouseup happened outside of the window.  This property is set to true
14779      * if outer handles are defined.
14780      *
14781      * @property hasOuterHandles
14782      * @type boolean
14783      * @default false
14784      */
14785     hasOuterHandles: false,
14786
14787     /**
14788      * Code that executes immediately before the startDrag event
14789      * @method b4StartDrag
14790      * @private
14791      */
14792     b4StartDrag: function(x, y) { },
14793
14794     /**
14795      * Abstract method called after a drag/drop object is clicked
14796      * and the drag or mousedown time thresholds have beeen met.
14797      * @method startDrag
14798      * @param {int} X click location
14799      * @param {int} Y click location
14800      */
14801     startDrag: function(x, y) { /* override this */ },
14802
14803     /**
14804      * Code that executes immediately before the onDrag event
14805      * @method b4Drag
14806      * @private
14807      */
14808     b4Drag: function(e) { },
14809
14810     /**
14811      * Abstract method called during the onMouseMove event while dragging an
14812      * object.
14813      * @method onDrag
14814      * @param {Event} e the mousemove event
14815      */
14816     onDrag: function(e) { /* override this */ },
14817
14818     /**
14819      * Abstract method called when this element fist begins hovering over
14820      * another DragDrop obj
14821      * @method onDragEnter
14822      * @param {Event} e the mousemove event
14823      * @param {String|DragDrop[]} id In POINT mode, the element
14824      * id this is hovering over.  In INTERSECT mode, an array of one or more
14825      * dragdrop items being hovered over.
14826      */
14827     onDragEnter: function(e, id) { /* override this */ },
14828
14829     /**
14830      * Code that executes immediately before the onDragOver event
14831      * @method b4DragOver
14832      * @private
14833      */
14834     b4DragOver: function(e) { },
14835
14836     /**
14837      * Abstract method called when this element is hovering over another
14838      * DragDrop obj
14839      * @method onDragOver
14840      * @param {Event} e the mousemove event
14841      * @param {String|DragDrop[]} id In POINT mode, the element
14842      * id this is hovering over.  In INTERSECT mode, an array of dd items
14843      * being hovered over.
14844      */
14845     onDragOver: function(e, id) { /* override this */ },
14846
14847     /**
14848      * Code that executes immediately before the onDragOut event
14849      * @method b4DragOut
14850      * @private
14851      */
14852     b4DragOut: function(e) { },
14853
14854     /**
14855      * Abstract method called when we are no longer hovering over an element
14856      * @method onDragOut
14857      * @param {Event} e the mousemove event
14858      * @param {String|DragDrop[]} id In POINT mode, the element
14859      * id this was hovering over.  In INTERSECT mode, an array of dd items
14860      * that the mouse is no longer over.
14861      */
14862     onDragOut: function(e, id) { /* override this */ },
14863
14864     /**
14865      * Code that executes immediately before the onDragDrop event
14866      * @method b4DragDrop
14867      * @private
14868      */
14869     b4DragDrop: function(e) { },
14870
14871     /**
14872      * Abstract method called when this item is dropped on another DragDrop
14873      * obj
14874      * @method onDragDrop
14875      * @param {Event} e the mouseup event
14876      * @param {String|DragDrop[]} id In POINT mode, the element
14877      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14878      * was dropped on.
14879      */
14880     onDragDrop: function(e, id) { /* override this */ },
14881
14882     /**
14883      * Abstract method called when this item is dropped on an area with no
14884      * drop target
14885      * @method onInvalidDrop
14886      * @param {Event} e the mouseup event
14887      */
14888     onInvalidDrop: function(e) { /* override this */ },
14889
14890     /**
14891      * Code that executes immediately before the endDrag event
14892      * @method b4EndDrag
14893      * @private
14894      */
14895     b4EndDrag: function(e) { },
14896
14897     /**
14898      * Fired when we are done dragging the object
14899      * @method endDrag
14900      * @param {Event} e the mouseup event
14901      */
14902     endDrag: function(e) { /* override this */ },
14903
14904     /**
14905      * Code executed immediately before the onMouseDown event
14906      * @method b4MouseDown
14907      * @param {Event} e the mousedown event
14908      * @private
14909      */
14910     b4MouseDown: function(e) {  },
14911
14912     /**
14913      * Event handler that fires when a drag/drop obj gets a mousedown
14914      * @method onMouseDown
14915      * @param {Event} e the mousedown event
14916      */
14917     onMouseDown: function(e) { /* override this */ },
14918
14919     /**
14920      * Event handler that fires when a drag/drop obj gets a mouseup
14921      * @method onMouseUp
14922      * @param {Event} e the mouseup event
14923      */
14924     onMouseUp: function(e) { /* override this */ },
14925
14926     /**
14927      * Override the onAvailable method to do what is needed after the initial
14928      * position was determined.
14929      * @method onAvailable
14930      */
14931     onAvailable: function () {
14932     },
14933
14934     /*
14935      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14936      * @type Object
14937      */
14938     defaultPadding : {left:0, right:0, top:0, bottom:0},
14939
14940     /*
14941      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14942  *
14943  * Usage:
14944  <pre><code>
14945  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14946                 { dragElId: "existingProxyDiv" });
14947  dd.startDrag = function(){
14948      this.constrainTo("parent-id");
14949  };
14950  </code></pre>
14951  * Or you can initalize it using the {@link Roo.Element} object:
14952  <pre><code>
14953  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14954      startDrag : function(){
14955          this.constrainTo("parent-id");
14956      }
14957  });
14958  </code></pre>
14959      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14960      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14961      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14962      * an object containing the sides to pad. For example: {right:10, bottom:10}
14963      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14964      */
14965     constrainTo : function(constrainTo, pad, inContent){
14966         if(typeof pad == "number"){
14967             pad = {left: pad, right:pad, top:pad, bottom:pad};
14968         }
14969         pad = pad || this.defaultPadding;
14970         var b = Roo.get(this.getEl()).getBox();
14971         var ce = Roo.get(constrainTo);
14972         var s = ce.getScroll();
14973         var c, cd = ce.dom;
14974         if(cd == document.body){
14975             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14976         }else{
14977             xy = ce.getXY();
14978             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14979         }
14980
14981
14982         var topSpace = b.y - c.y;
14983         var leftSpace = b.x - c.x;
14984
14985         this.resetConstraints();
14986         this.setXConstraint(leftSpace - (pad.left||0), // left
14987                 c.width - leftSpace - b.width - (pad.right||0) //right
14988         );
14989         this.setYConstraint(topSpace - (pad.top||0), //top
14990                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14991         );
14992     },
14993
14994     /**
14995      * Returns a reference to the linked element
14996      * @method getEl
14997      * @return {HTMLElement} the html element
14998      */
14999     getEl: function() {
15000         if (!this._domRef) {
15001             this._domRef = Roo.getDom(this.id);
15002         }
15003
15004         return this._domRef;
15005     },
15006
15007     /**
15008      * Returns a reference to the actual element to drag.  By default this is
15009      * the same as the html element, but it can be assigned to another
15010      * element. An example of this can be found in Roo.dd.DDProxy
15011      * @method getDragEl
15012      * @return {HTMLElement} the html element
15013      */
15014     getDragEl: function() {
15015         return Roo.getDom(this.dragElId);
15016     },
15017
15018     /**
15019      * Sets up the DragDrop object.  Must be called in the constructor of any
15020      * Roo.dd.DragDrop subclass
15021      * @method init
15022      * @param id the id of the linked element
15023      * @param {String} sGroup the group of related items
15024      * @param {object} config configuration attributes
15025      */
15026     init: function(id, sGroup, config) {
15027         this.initTarget(id, sGroup, config);
15028         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15029         // Event.on(this.id, "selectstart", Event.preventDefault);
15030     },
15031
15032     /**
15033      * Initializes Targeting functionality only... the object does not
15034      * get a mousedown handler.
15035      * @method initTarget
15036      * @param id the id of the linked element
15037      * @param {String} sGroup the group of related items
15038      * @param {object} config configuration attributes
15039      */
15040     initTarget: function(id, sGroup, config) {
15041
15042         // configuration attributes
15043         this.config = config || {};
15044
15045         // create a local reference to the drag and drop manager
15046         this.DDM = Roo.dd.DDM;
15047         // initialize the groups array
15048         this.groups = {};
15049
15050         // assume that we have an element reference instead of an id if the
15051         // parameter is not a string
15052         if (typeof id !== "string") {
15053             id = Roo.id(id);
15054         }
15055
15056         // set the id
15057         this.id = id;
15058
15059         // add to an interaction group
15060         this.addToGroup((sGroup) ? sGroup : "default");
15061
15062         // We don't want to register this as the handle with the manager
15063         // so we just set the id rather than calling the setter.
15064         this.handleElId = id;
15065
15066         // the linked element is the element that gets dragged by default
15067         this.setDragElId(id);
15068
15069         // by default, clicked anchors will not start drag operations.
15070         this.invalidHandleTypes = { A: "A" };
15071         this.invalidHandleIds = {};
15072         this.invalidHandleClasses = [];
15073
15074         this.applyConfig();
15075
15076         this.handleOnAvailable();
15077     },
15078
15079     /**
15080      * Applies the configuration parameters that were passed into the constructor.
15081      * This is supposed to happen at each level through the inheritance chain.  So
15082      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15083      * DragDrop in order to get all of the parameters that are available in
15084      * each object.
15085      * @method applyConfig
15086      */
15087     applyConfig: function() {
15088
15089         // configurable properties:
15090         //    padding, isTarget, maintainOffset, primaryButtonOnly
15091         this.padding           = this.config.padding || [0, 0, 0, 0];
15092         this.isTarget          = (this.config.isTarget !== false);
15093         this.maintainOffset    = (this.config.maintainOffset);
15094         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15095
15096     },
15097
15098     /**
15099      * Executed when the linked element is available
15100      * @method handleOnAvailable
15101      * @private
15102      */
15103     handleOnAvailable: function() {
15104         this.available = true;
15105         this.resetConstraints();
15106         this.onAvailable();
15107     },
15108
15109      /**
15110      * Configures the padding for the target zone in px.  Effectively expands
15111      * (or reduces) the virtual object size for targeting calculations.
15112      * Supports css-style shorthand; if only one parameter is passed, all sides
15113      * will have that padding, and if only two are passed, the top and bottom
15114      * will have the first param, the left and right the second.
15115      * @method setPadding
15116      * @param {int} iTop    Top pad
15117      * @param {int} iRight  Right pad
15118      * @param {int} iBot    Bot pad
15119      * @param {int} iLeft   Left pad
15120      */
15121     setPadding: function(iTop, iRight, iBot, iLeft) {
15122         // this.padding = [iLeft, iRight, iTop, iBot];
15123         if (!iRight && 0 !== iRight) {
15124             this.padding = [iTop, iTop, iTop, iTop];
15125         } else if (!iBot && 0 !== iBot) {
15126             this.padding = [iTop, iRight, iTop, iRight];
15127         } else {
15128             this.padding = [iTop, iRight, iBot, iLeft];
15129         }
15130     },
15131
15132     /**
15133      * Stores the initial placement of the linked element.
15134      * @method setInitialPosition
15135      * @param {int} diffX   the X offset, default 0
15136      * @param {int} diffY   the Y offset, default 0
15137      */
15138     setInitPosition: function(diffX, diffY) {
15139         var el = this.getEl();
15140
15141         if (!this.DDM.verifyEl(el)) {
15142             return;
15143         }
15144
15145         var dx = diffX || 0;
15146         var dy = diffY || 0;
15147
15148         var p = Dom.getXY( el );
15149
15150         this.initPageX = p[0] - dx;
15151         this.initPageY = p[1] - dy;
15152
15153         this.lastPageX = p[0];
15154         this.lastPageY = p[1];
15155
15156
15157         this.setStartPosition(p);
15158     },
15159
15160     /**
15161      * Sets the start position of the element.  This is set when the obj
15162      * is initialized, the reset when a drag is started.
15163      * @method setStartPosition
15164      * @param pos current position (from previous lookup)
15165      * @private
15166      */
15167     setStartPosition: function(pos) {
15168         var p = pos || Dom.getXY( this.getEl() );
15169         this.deltaSetXY = null;
15170
15171         this.startPageX = p[0];
15172         this.startPageY = p[1];
15173     },
15174
15175     /**
15176      * Add this instance to a group of related drag/drop objects.  All
15177      * instances belong to at least one group, and can belong to as many
15178      * groups as needed.
15179      * @method addToGroup
15180      * @param sGroup {string} the name of the group
15181      */
15182     addToGroup: function(sGroup) {
15183         this.groups[sGroup] = true;
15184         this.DDM.regDragDrop(this, sGroup);
15185     },
15186
15187     /**
15188      * Remove's this instance from the supplied interaction group
15189      * @method removeFromGroup
15190      * @param {string}  sGroup  The group to drop
15191      */
15192     removeFromGroup: function(sGroup) {
15193         if (this.groups[sGroup]) {
15194             delete this.groups[sGroup];
15195         }
15196
15197         this.DDM.removeDDFromGroup(this, sGroup);
15198     },
15199
15200     /**
15201      * Allows you to specify that an element other than the linked element
15202      * will be moved with the cursor during a drag
15203      * @method setDragElId
15204      * @param id {string} the id of the element that will be used to initiate the drag
15205      */
15206     setDragElId: function(id) {
15207         this.dragElId = id;
15208     },
15209
15210     /**
15211      * Allows you to specify a child of the linked element that should be
15212      * used to initiate the drag operation.  An example of this would be if
15213      * you have a content div with text and links.  Clicking anywhere in the
15214      * content area would normally start the drag operation.  Use this method
15215      * to specify that an element inside of the content div is the element
15216      * that starts the drag operation.
15217      * @method setHandleElId
15218      * @param id {string} the id of the element that will be used to
15219      * initiate the drag.
15220      */
15221     setHandleElId: function(id) {
15222         if (typeof id !== "string") {
15223             id = Roo.id(id);
15224         }
15225         this.handleElId = id;
15226         this.DDM.regHandle(this.id, id);
15227     },
15228
15229     /**
15230      * Allows you to set an element outside of the linked element as a drag
15231      * handle
15232      * @method setOuterHandleElId
15233      * @param id the id of the element that will be used to initiate the drag
15234      */
15235     setOuterHandleElId: function(id) {
15236         if (typeof id !== "string") {
15237             id = Roo.id(id);
15238         }
15239         Event.on(id, "mousedown",
15240                 this.handleMouseDown, this);
15241         this.setHandleElId(id);
15242
15243         this.hasOuterHandles = true;
15244     },
15245
15246     /**
15247      * Remove all drag and drop hooks for this element
15248      * @method unreg
15249      */
15250     unreg: function() {
15251         Event.un(this.id, "mousedown",
15252                 this.handleMouseDown);
15253         this._domRef = null;
15254         this.DDM._remove(this);
15255     },
15256
15257     destroy : function(){
15258         this.unreg();
15259     },
15260
15261     /**
15262      * Returns true if this instance is locked, or the drag drop mgr is locked
15263      * (meaning that all drag/drop is disabled on the page.)
15264      * @method isLocked
15265      * @return {boolean} true if this obj or all drag/drop is locked, else
15266      * false
15267      */
15268     isLocked: function() {
15269         return (this.DDM.isLocked() || this.locked);
15270     },
15271
15272     /**
15273      * Fired when this object is clicked
15274      * @method handleMouseDown
15275      * @param {Event} e
15276      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15277      * @private
15278      */
15279     handleMouseDown: function(e, oDD){
15280         if (this.primaryButtonOnly && e.button != 0) {
15281             return;
15282         }
15283
15284         if (this.isLocked()) {
15285             return;
15286         }
15287
15288         this.DDM.refreshCache(this.groups);
15289
15290         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15291         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15292         } else {
15293             if (this.clickValidator(e)) {
15294
15295                 // set the initial element position
15296                 this.setStartPosition();
15297
15298
15299                 this.b4MouseDown(e);
15300                 this.onMouseDown(e);
15301
15302                 this.DDM.handleMouseDown(e, this);
15303
15304                 this.DDM.stopEvent(e);
15305             } else {
15306
15307
15308             }
15309         }
15310     },
15311
15312     clickValidator: function(e) {
15313         var target = e.getTarget();
15314         return ( this.isValidHandleChild(target) &&
15315                     (this.id == this.handleElId ||
15316                         this.DDM.handleWasClicked(target, this.id)) );
15317     },
15318
15319     /**
15320      * Allows you to specify a tag name that should not start a drag operation
15321      * when clicked.  This is designed to facilitate embedding links within a
15322      * drag handle that do something other than start the drag.
15323      * @method addInvalidHandleType
15324      * @param {string} tagName the type of element to exclude
15325      */
15326     addInvalidHandleType: function(tagName) {
15327         var type = tagName.toUpperCase();
15328         this.invalidHandleTypes[type] = type;
15329     },
15330
15331     /**
15332      * Lets you to specify an element id for a child of a drag handle
15333      * that should not initiate a drag
15334      * @method addInvalidHandleId
15335      * @param {string} id the element id of the element you wish to ignore
15336      */
15337     addInvalidHandleId: function(id) {
15338         if (typeof id !== "string") {
15339             id = Roo.id(id);
15340         }
15341         this.invalidHandleIds[id] = id;
15342     },
15343
15344     /**
15345      * Lets you specify a css class of elements that will not initiate a drag
15346      * @method addInvalidHandleClass
15347      * @param {string} cssClass the class of the elements you wish to ignore
15348      */
15349     addInvalidHandleClass: function(cssClass) {
15350         this.invalidHandleClasses.push(cssClass);
15351     },
15352
15353     /**
15354      * Unsets an excluded tag name set by addInvalidHandleType
15355      * @method removeInvalidHandleType
15356      * @param {string} tagName the type of element to unexclude
15357      */
15358     removeInvalidHandleType: function(tagName) {
15359         var type = tagName.toUpperCase();
15360         // this.invalidHandleTypes[type] = null;
15361         delete this.invalidHandleTypes[type];
15362     },
15363
15364     /**
15365      * Unsets an invalid handle id
15366      * @method removeInvalidHandleId
15367      * @param {string} id the id of the element to re-enable
15368      */
15369     removeInvalidHandleId: function(id) {
15370         if (typeof id !== "string") {
15371             id = Roo.id(id);
15372         }
15373         delete this.invalidHandleIds[id];
15374     },
15375
15376     /**
15377      * Unsets an invalid css class
15378      * @method removeInvalidHandleClass
15379      * @param {string} cssClass the class of the element(s) you wish to
15380      * re-enable
15381      */
15382     removeInvalidHandleClass: function(cssClass) {
15383         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15384             if (this.invalidHandleClasses[i] == cssClass) {
15385                 delete this.invalidHandleClasses[i];
15386             }
15387         }
15388     },
15389
15390     /**
15391      * Checks the tag exclusion list to see if this click should be ignored
15392      * @method isValidHandleChild
15393      * @param {HTMLElement} node the HTMLElement to evaluate
15394      * @return {boolean} true if this is a valid tag type, false if not
15395      */
15396     isValidHandleChild: function(node) {
15397
15398         var valid = true;
15399         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15400         var nodeName;
15401         try {
15402             nodeName = node.nodeName.toUpperCase();
15403         } catch(e) {
15404             nodeName = node.nodeName;
15405         }
15406         valid = valid && !this.invalidHandleTypes[nodeName];
15407         valid = valid && !this.invalidHandleIds[node.id];
15408
15409         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15410             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15411         }
15412
15413
15414         return valid;
15415
15416     },
15417
15418     /**
15419      * Create the array of horizontal tick marks if an interval was specified
15420      * in setXConstraint().
15421      * @method setXTicks
15422      * @private
15423      */
15424     setXTicks: function(iStartX, iTickSize) {
15425         this.xTicks = [];
15426         this.xTickSize = iTickSize;
15427
15428         var tickMap = {};
15429
15430         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15431             if (!tickMap[i]) {
15432                 this.xTicks[this.xTicks.length] = i;
15433                 tickMap[i] = true;
15434             }
15435         }
15436
15437         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15438             if (!tickMap[i]) {
15439                 this.xTicks[this.xTicks.length] = i;
15440                 tickMap[i] = true;
15441             }
15442         }
15443
15444         this.xTicks.sort(this.DDM.numericSort) ;
15445     },
15446
15447     /**
15448      * Create the array of vertical tick marks if an interval was specified in
15449      * setYConstraint().
15450      * @method setYTicks
15451      * @private
15452      */
15453     setYTicks: function(iStartY, iTickSize) {
15454         this.yTicks = [];
15455         this.yTickSize = iTickSize;
15456
15457         var tickMap = {};
15458
15459         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15460             if (!tickMap[i]) {
15461                 this.yTicks[this.yTicks.length] = i;
15462                 tickMap[i] = true;
15463             }
15464         }
15465
15466         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15467             if (!tickMap[i]) {
15468                 this.yTicks[this.yTicks.length] = i;
15469                 tickMap[i] = true;
15470             }
15471         }
15472
15473         this.yTicks.sort(this.DDM.numericSort) ;
15474     },
15475
15476     /**
15477      * By default, the element can be dragged any place on the screen.  Use
15478      * this method to limit the horizontal travel of the element.  Pass in
15479      * 0,0 for the parameters if you want to lock the drag to the y axis.
15480      * @method setXConstraint
15481      * @param {int} iLeft the number of pixels the element can move to the left
15482      * @param {int} iRight the number of pixels the element can move to the
15483      * right
15484      * @param {int} iTickSize optional parameter for specifying that the
15485      * element
15486      * should move iTickSize pixels at a time.
15487      */
15488     setXConstraint: function(iLeft, iRight, iTickSize) {
15489         this.leftConstraint = iLeft;
15490         this.rightConstraint = iRight;
15491
15492         this.minX = this.initPageX - iLeft;
15493         this.maxX = this.initPageX + iRight;
15494         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15495
15496         this.constrainX = true;
15497     },
15498
15499     /**
15500      * Clears any constraints applied to this instance.  Also clears ticks
15501      * since they can't exist independent of a constraint at this time.
15502      * @method clearConstraints
15503      */
15504     clearConstraints: function() {
15505         this.constrainX = false;
15506         this.constrainY = false;
15507         this.clearTicks();
15508     },
15509
15510     /**
15511      * Clears any tick interval defined for this instance
15512      * @method clearTicks
15513      */
15514     clearTicks: function() {
15515         this.xTicks = null;
15516         this.yTicks = null;
15517         this.xTickSize = 0;
15518         this.yTickSize = 0;
15519     },
15520
15521     /**
15522      * By default, the element can be dragged any place on the screen.  Set
15523      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15524      * parameters if you want to lock the drag to the x axis.
15525      * @method setYConstraint
15526      * @param {int} iUp the number of pixels the element can move up
15527      * @param {int} iDown the number of pixels the element can move down
15528      * @param {int} iTickSize optional parameter for specifying that the
15529      * element should move iTickSize pixels at a time.
15530      */
15531     setYConstraint: function(iUp, iDown, iTickSize) {
15532         this.topConstraint = iUp;
15533         this.bottomConstraint = iDown;
15534
15535         this.minY = this.initPageY - iUp;
15536         this.maxY = this.initPageY + iDown;
15537         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15538
15539         this.constrainY = true;
15540
15541     },
15542
15543     /**
15544      * resetConstraints must be called if you manually reposition a dd element.
15545      * @method resetConstraints
15546      * @param {boolean} maintainOffset
15547      */
15548     resetConstraints: function() {
15549
15550
15551         // Maintain offsets if necessary
15552         if (this.initPageX || this.initPageX === 0) {
15553             // figure out how much this thing has moved
15554             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15555             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15556
15557             this.setInitPosition(dx, dy);
15558
15559         // This is the first time we have detected the element's position
15560         } else {
15561             this.setInitPosition();
15562         }
15563
15564         if (this.constrainX) {
15565             this.setXConstraint( this.leftConstraint,
15566                                  this.rightConstraint,
15567                                  this.xTickSize        );
15568         }
15569
15570         if (this.constrainY) {
15571             this.setYConstraint( this.topConstraint,
15572                                  this.bottomConstraint,
15573                                  this.yTickSize         );
15574         }
15575     },
15576
15577     /**
15578      * Normally the drag element is moved pixel by pixel, but we can specify
15579      * that it move a number of pixels at a time.  This method resolves the
15580      * location when we have it set up like this.
15581      * @method getTick
15582      * @param {int} val where we want to place the object
15583      * @param {int[]} tickArray sorted array of valid points
15584      * @return {int} the closest tick
15585      * @private
15586      */
15587     getTick: function(val, tickArray) {
15588
15589         if (!tickArray) {
15590             // If tick interval is not defined, it is effectively 1 pixel,
15591             // so we return the value passed to us.
15592             return val;
15593         } else if (tickArray[0] >= val) {
15594             // The value is lower than the first tick, so we return the first
15595             // tick.
15596             return tickArray[0];
15597         } else {
15598             for (var i=0, len=tickArray.length; i<len; ++i) {
15599                 var next = i + 1;
15600                 if (tickArray[next] && tickArray[next] >= val) {
15601                     var diff1 = val - tickArray[i];
15602                     var diff2 = tickArray[next] - val;
15603                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15604                 }
15605             }
15606
15607             // The value is larger than the last tick, so we return the last
15608             // tick.
15609             return tickArray[tickArray.length - 1];
15610         }
15611     },
15612
15613     /**
15614      * toString method
15615      * @method toString
15616      * @return {string} string representation of the dd obj
15617      */
15618     toString: function() {
15619         return ("DragDrop " + this.id);
15620     }
15621
15622 });
15623
15624 })();
15625 /*
15626  * Based on:
15627  * Ext JS Library 1.1.1
15628  * Copyright(c) 2006-2007, Ext JS, LLC.
15629  *
15630  * Originally Released Under LGPL - original licence link has changed is not relivant.
15631  *
15632  * Fork - LGPL
15633  * <script type="text/javascript">
15634  */
15635
15636
15637 /**
15638  * The drag and drop utility provides a framework for building drag and drop
15639  * applications.  In addition to enabling drag and drop for specific elements,
15640  * the drag and drop elements are tracked by the manager class, and the
15641  * interactions between the various elements are tracked during the drag and
15642  * the implementing code is notified about these important moments.
15643  */
15644
15645 // Only load the library once.  Rewriting the manager class would orphan
15646 // existing drag and drop instances.
15647 if (!Roo.dd.DragDropMgr) {
15648
15649 /**
15650  * @class Roo.dd.DragDropMgr
15651  * DragDropMgr is a singleton that tracks the element interaction for
15652  * all DragDrop items in the window.  Generally, you will not call
15653  * this class directly, but it does have helper methods that could
15654  * be useful in your DragDrop implementations.
15655  * @singleton
15656  */
15657 Roo.dd.DragDropMgr = function() {
15658
15659     var Event = Roo.EventManager;
15660
15661     return {
15662
15663         /**
15664          * Two dimensional Array of registered DragDrop objects.  The first
15665          * dimension is the DragDrop item group, the second the DragDrop
15666          * object.
15667          * @property ids
15668          * @type {string: string}
15669          * @private
15670          * @static
15671          */
15672         ids: {},
15673
15674         /**
15675          * Array of element ids defined as drag handles.  Used to determine
15676          * if the element that generated the mousedown event is actually the
15677          * handle and not the html element itself.
15678          * @property handleIds
15679          * @type {string: string}
15680          * @private
15681          * @static
15682          */
15683         handleIds: {},
15684
15685         /**
15686          * the DragDrop object that is currently being dragged
15687          * @property dragCurrent
15688          * @type DragDrop
15689          * @private
15690          * @static
15691          **/
15692         dragCurrent: null,
15693
15694         /**
15695          * the DragDrop object(s) that are being hovered over
15696          * @property dragOvers
15697          * @type Array
15698          * @private
15699          * @static
15700          */
15701         dragOvers: {},
15702
15703         /**
15704          * the X distance between the cursor and the object being dragged
15705          * @property deltaX
15706          * @type int
15707          * @private
15708          * @static
15709          */
15710         deltaX: 0,
15711
15712         /**
15713          * the Y distance between the cursor and the object being dragged
15714          * @property deltaY
15715          * @type int
15716          * @private
15717          * @static
15718          */
15719         deltaY: 0,
15720
15721         /**
15722          * Flag to determine if we should prevent the default behavior of the
15723          * events we define. By default this is true, but this can be set to
15724          * false if you need the default behavior (not recommended)
15725          * @property preventDefault
15726          * @type boolean
15727          * @static
15728          */
15729         preventDefault: true,
15730
15731         /**
15732          * Flag to determine if we should stop the propagation of the events
15733          * we generate. This is true by default but you may want to set it to
15734          * false if the html element contains other features that require the
15735          * mouse click.
15736          * @property stopPropagation
15737          * @type boolean
15738          * @static
15739          */
15740         stopPropagation: true,
15741
15742         /**
15743          * Internal flag that is set to true when drag and drop has been
15744          * intialized
15745          * @property initialized
15746          * @private
15747          * @static
15748          */
15749         initalized: false,
15750
15751         /**
15752          * All drag and drop can be disabled.
15753          * @property locked
15754          * @private
15755          * @static
15756          */
15757         locked: false,
15758
15759         /**
15760          * Called the first time an element is registered.
15761          * @method init
15762          * @private
15763          * @static
15764          */
15765         init: function() {
15766             this.initialized = true;
15767         },
15768
15769         /**
15770          * In point mode, drag and drop interaction is defined by the
15771          * location of the cursor during the drag/drop
15772          * @property POINT
15773          * @type int
15774          * @static
15775          */
15776         POINT: 0,
15777
15778         /**
15779          * In intersect mode, drag and drop interactio nis defined by the
15780          * overlap of two or more drag and drop objects.
15781          * @property INTERSECT
15782          * @type int
15783          * @static
15784          */
15785         INTERSECT: 1,
15786
15787         /**
15788          * The current drag and drop mode.  Default: POINT
15789          * @property mode
15790          * @type int
15791          * @static
15792          */
15793         mode: 0,
15794
15795         /**
15796          * Runs method on all drag and drop objects
15797          * @method _execOnAll
15798          * @private
15799          * @static
15800          */
15801         _execOnAll: function(sMethod, args) {
15802             for (var i in this.ids) {
15803                 for (var j in this.ids[i]) {
15804                     var oDD = this.ids[i][j];
15805                     if (! this.isTypeOfDD(oDD)) {
15806                         continue;
15807                     }
15808                     oDD[sMethod].apply(oDD, args);
15809                 }
15810             }
15811         },
15812
15813         /**
15814          * Drag and drop initialization.  Sets up the global event handlers
15815          * @method _onLoad
15816          * @private
15817          * @static
15818          */
15819         _onLoad: function() {
15820
15821             this.init();
15822
15823
15824             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15825             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15826             Event.on(window,   "unload",    this._onUnload, this, true);
15827             Event.on(window,   "resize",    this._onResize, this, true);
15828             // Event.on(window,   "mouseout",    this._test);
15829
15830         },
15831
15832         /**
15833          * Reset constraints on all drag and drop objs
15834          * @method _onResize
15835          * @private
15836          * @static
15837          */
15838         _onResize: function(e) {
15839             this._execOnAll("resetConstraints", []);
15840         },
15841
15842         /**
15843          * Lock all drag and drop functionality
15844          * @method lock
15845          * @static
15846          */
15847         lock: function() { this.locked = true; },
15848
15849         /**
15850          * Unlock all drag and drop functionality
15851          * @method unlock
15852          * @static
15853          */
15854         unlock: function() { this.locked = false; },
15855
15856         /**
15857          * Is drag and drop locked?
15858          * @method isLocked
15859          * @return {boolean} True if drag and drop is locked, false otherwise.
15860          * @static
15861          */
15862         isLocked: function() { return this.locked; },
15863
15864         /**
15865          * Location cache that is set for all drag drop objects when a drag is
15866          * initiated, cleared when the drag is finished.
15867          * @property locationCache
15868          * @private
15869          * @static
15870          */
15871         locationCache: {},
15872
15873         /**
15874          * Set useCache to false if you want to force object the lookup of each
15875          * drag and drop linked element constantly during a drag.
15876          * @property useCache
15877          * @type boolean
15878          * @static
15879          */
15880         useCache: true,
15881
15882         /**
15883          * The number of pixels that the mouse needs to move after the
15884          * mousedown before the drag is initiated.  Default=3;
15885          * @property clickPixelThresh
15886          * @type int
15887          * @static
15888          */
15889         clickPixelThresh: 3,
15890
15891         /**
15892          * The number of milliseconds after the mousedown event to initiate the
15893          * drag if we don't get a mouseup event. Default=1000
15894          * @property clickTimeThresh
15895          * @type int
15896          * @static
15897          */
15898         clickTimeThresh: 350,
15899
15900         /**
15901          * Flag that indicates that either the drag pixel threshold or the
15902          * mousdown time threshold has been met
15903          * @property dragThreshMet
15904          * @type boolean
15905          * @private
15906          * @static
15907          */
15908         dragThreshMet: false,
15909
15910         /**
15911          * Timeout used for the click time threshold
15912          * @property clickTimeout
15913          * @type Object
15914          * @private
15915          * @static
15916          */
15917         clickTimeout: null,
15918
15919         /**
15920          * The X position of the mousedown event stored for later use when a
15921          * drag threshold is met.
15922          * @property startX
15923          * @type int
15924          * @private
15925          * @static
15926          */
15927         startX: 0,
15928
15929         /**
15930          * The Y position of the mousedown event stored for later use when a
15931          * drag threshold is met.
15932          * @property startY
15933          * @type int
15934          * @private
15935          * @static
15936          */
15937         startY: 0,
15938
15939         /**
15940          * Each DragDrop instance must be registered with the DragDropMgr.
15941          * This is executed in DragDrop.init()
15942          * @method regDragDrop
15943          * @param {DragDrop} oDD the DragDrop object to register
15944          * @param {String} sGroup the name of the group this element belongs to
15945          * @static
15946          */
15947         regDragDrop: function(oDD, sGroup) {
15948             if (!this.initialized) { this.init(); }
15949
15950             if (!this.ids[sGroup]) {
15951                 this.ids[sGroup] = {};
15952             }
15953             this.ids[sGroup][oDD.id] = oDD;
15954         },
15955
15956         /**
15957          * Removes the supplied dd instance from the supplied group. Executed
15958          * by DragDrop.removeFromGroup, so don't call this function directly.
15959          * @method removeDDFromGroup
15960          * @private
15961          * @static
15962          */
15963         removeDDFromGroup: function(oDD, sGroup) {
15964             if (!this.ids[sGroup]) {
15965                 this.ids[sGroup] = {};
15966             }
15967
15968             var obj = this.ids[sGroup];
15969             if (obj && obj[oDD.id]) {
15970                 delete obj[oDD.id];
15971             }
15972         },
15973
15974         /**
15975          * Unregisters a drag and drop item.  This is executed in
15976          * DragDrop.unreg, use that method instead of calling this directly.
15977          * @method _remove
15978          * @private
15979          * @static
15980          */
15981         _remove: function(oDD) {
15982             for (var g in oDD.groups) {
15983                 if (g && this.ids[g][oDD.id]) {
15984                     delete this.ids[g][oDD.id];
15985                 }
15986             }
15987             delete this.handleIds[oDD.id];
15988         },
15989
15990         /**
15991          * Each DragDrop handle element must be registered.  This is done
15992          * automatically when executing DragDrop.setHandleElId()
15993          * @method regHandle
15994          * @param {String} sDDId the DragDrop id this element is a handle for
15995          * @param {String} sHandleId the id of the element that is the drag
15996          * handle
15997          * @static
15998          */
15999         regHandle: function(sDDId, sHandleId) {
16000             if (!this.handleIds[sDDId]) {
16001                 this.handleIds[sDDId] = {};
16002             }
16003             this.handleIds[sDDId][sHandleId] = sHandleId;
16004         },
16005
16006         /**
16007          * Utility function to determine if a given element has been
16008          * registered as a drag drop item.
16009          * @method isDragDrop
16010          * @param {String} id the element id to check
16011          * @return {boolean} true if this element is a DragDrop item,
16012          * false otherwise
16013          * @static
16014          */
16015         isDragDrop: function(id) {
16016             return ( this.getDDById(id) ) ? true : false;
16017         },
16018
16019         /**
16020          * Returns the drag and drop instances that are in all groups the
16021          * passed in instance belongs to.
16022          * @method getRelated
16023          * @param {DragDrop} p_oDD the obj to get related data for
16024          * @param {boolean} bTargetsOnly if true, only return targetable objs
16025          * @return {DragDrop[]} the related instances
16026          * @static
16027          */
16028         getRelated: function(p_oDD, bTargetsOnly) {
16029             var oDDs = [];
16030             for (var i in p_oDD.groups) {
16031                 for (j in this.ids[i]) {
16032                     var dd = this.ids[i][j];
16033                     if (! this.isTypeOfDD(dd)) {
16034                         continue;
16035                     }
16036                     if (!bTargetsOnly || dd.isTarget) {
16037                         oDDs[oDDs.length] = dd;
16038                     }
16039                 }
16040             }
16041
16042             return oDDs;
16043         },
16044
16045         /**
16046          * Returns true if the specified dd target is a legal target for
16047          * the specifice drag obj
16048          * @method isLegalTarget
16049          * @param {DragDrop} the drag obj
16050          * @param {DragDrop} the target
16051          * @return {boolean} true if the target is a legal target for the
16052          * dd obj
16053          * @static
16054          */
16055         isLegalTarget: function (oDD, oTargetDD) {
16056             var targets = this.getRelated(oDD, true);
16057             for (var i=0, len=targets.length;i<len;++i) {
16058                 if (targets[i].id == oTargetDD.id) {
16059                     return true;
16060                 }
16061             }
16062
16063             return false;
16064         },
16065
16066         /**
16067          * My goal is to be able to transparently determine if an object is
16068          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16069          * returns "object", oDD.constructor.toString() always returns
16070          * "DragDrop" and not the name of the subclass.  So for now it just
16071          * evaluates a well-known variable in DragDrop.
16072          * @method isTypeOfDD
16073          * @param {Object} the object to evaluate
16074          * @return {boolean} true if typeof oDD = DragDrop
16075          * @static
16076          */
16077         isTypeOfDD: function (oDD) {
16078             return (oDD && oDD.__ygDragDrop);
16079         },
16080
16081         /**
16082          * Utility function to determine if a given element has been
16083          * registered as a drag drop handle for the given Drag Drop object.
16084          * @method isHandle
16085          * @param {String} id the element id to check
16086          * @return {boolean} true if this element is a DragDrop handle, false
16087          * otherwise
16088          * @static
16089          */
16090         isHandle: function(sDDId, sHandleId) {
16091             return ( this.handleIds[sDDId] &&
16092                             this.handleIds[sDDId][sHandleId] );
16093         },
16094
16095         /**
16096          * Returns the DragDrop instance for a given id
16097          * @method getDDById
16098          * @param {String} id the id of the DragDrop object
16099          * @return {DragDrop} the drag drop object, null if it is not found
16100          * @static
16101          */
16102         getDDById: function(id) {
16103             for (var i in this.ids) {
16104                 if (this.ids[i][id]) {
16105                     return this.ids[i][id];
16106                 }
16107             }
16108             return null;
16109         },
16110
16111         /**
16112          * Fired after a registered DragDrop object gets the mousedown event.
16113          * Sets up the events required to track the object being dragged
16114          * @method handleMouseDown
16115          * @param {Event} e the event
16116          * @param oDD the DragDrop object being dragged
16117          * @private
16118          * @static
16119          */
16120         handleMouseDown: function(e, oDD) {
16121             if(Roo.QuickTips){
16122                 Roo.QuickTips.disable();
16123             }
16124             this.currentTarget = e.getTarget();
16125
16126             this.dragCurrent = oDD;
16127
16128             var el = oDD.getEl();
16129
16130             // track start position
16131             this.startX = e.getPageX();
16132             this.startY = e.getPageY();
16133
16134             this.deltaX = this.startX - el.offsetLeft;
16135             this.deltaY = this.startY - el.offsetTop;
16136
16137             this.dragThreshMet = false;
16138
16139             this.clickTimeout = setTimeout(
16140                     function() {
16141                         var DDM = Roo.dd.DDM;
16142                         DDM.startDrag(DDM.startX, DDM.startY);
16143                     },
16144                     this.clickTimeThresh );
16145         },
16146
16147         /**
16148          * Fired when either the drag pixel threshol or the mousedown hold
16149          * time threshold has been met.
16150          * @method startDrag
16151          * @param x {int} the X position of the original mousedown
16152          * @param y {int} the Y position of the original mousedown
16153          * @static
16154          */
16155         startDrag: function(x, y) {
16156             clearTimeout(this.clickTimeout);
16157             if (this.dragCurrent) {
16158                 this.dragCurrent.b4StartDrag(x, y);
16159                 this.dragCurrent.startDrag(x, y);
16160             }
16161             this.dragThreshMet = true;
16162         },
16163
16164         /**
16165          * Internal function to handle the mouseup event.  Will be invoked
16166          * from the context of the document.
16167          * @method handleMouseUp
16168          * @param {Event} e the event
16169          * @private
16170          * @static
16171          */
16172         handleMouseUp: function(e) {
16173
16174             if(Roo.QuickTips){
16175                 Roo.QuickTips.enable();
16176             }
16177             if (! this.dragCurrent) {
16178                 return;
16179             }
16180
16181             clearTimeout(this.clickTimeout);
16182
16183             if (this.dragThreshMet) {
16184                 this.fireEvents(e, true);
16185             } else {
16186             }
16187
16188             this.stopDrag(e);
16189
16190             this.stopEvent(e);
16191         },
16192
16193         /**
16194          * Utility to stop event propagation and event default, if these
16195          * features are turned on.
16196          * @method stopEvent
16197          * @param {Event} e the event as returned by this.getEvent()
16198          * @static
16199          */
16200         stopEvent: function(e){
16201             if(this.stopPropagation) {
16202                 e.stopPropagation();
16203             }
16204
16205             if (this.preventDefault) {
16206                 e.preventDefault();
16207             }
16208         },
16209
16210         /**
16211          * Internal function to clean up event handlers after the drag
16212          * operation is complete
16213          * @method stopDrag
16214          * @param {Event} e the event
16215          * @private
16216          * @static
16217          */
16218         stopDrag: function(e) {
16219             // Fire the drag end event for the item that was dragged
16220             if (this.dragCurrent) {
16221                 if (this.dragThreshMet) {
16222                     this.dragCurrent.b4EndDrag(e);
16223                     this.dragCurrent.endDrag(e);
16224                 }
16225
16226                 this.dragCurrent.onMouseUp(e);
16227             }
16228
16229             this.dragCurrent = null;
16230             this.dragOvers = {};
16231         },
16232
16233         /**
16234          * Internal function to handle the mousemove event.  Will be invoked
16235          * from the context of the html element.
16236          *
16237          * @TODO figure out what we can do about mouse events lost when the
16238          * user drags objects beyond the window boundary.  Currently we can
16239          * detect this in internet explorer by verifying that the mouse is
16240          * down during the mousemove event.  Firefox doesn't give us the
16241          * button state on the mousemove event.
16242          * @method handleMouseMove
16243          * @param {Event} e the event
16244          * @private
16245          * @static
16246          */
16247         handleMouseMove: function(e) {
16248             if (! this.dragCurrent) {
16249                 return true;
16250             }
16251
16252             // var button = e.which || e.button;
16253
16254             // check for IE mouseup outside of page boundary
16255             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16256                 this.stopEvent(e);
16257                 return this.handleMouseUp(e);
16258             }
16259
16260             if (!this.dragThreshMet) {
16261                 var diffX = Math.abs(this.startX - e.getPageX());
16262                 var diffY = Math.abs(this.startY - e.getPageY());
16263                 if (diffX > this.clickPixelThresh ||
16264                             diffY > this.clickPixelThresh) {
16265                     this.startDrag(this.startX, this.startY);
16266                 }
16267             }
16268
16269             if (this.dragThreshMet) {
16270                 this.dragCurrent.b4Drag(e);
16271                 this.dragCurrent.onDrag(e);
16272                 if(!this.dragCurrent.moveOnly){
16273                     this.fireEvents(e, false);
16274                 }
16275             }
16276
16277             this.stopEvent(e);
16278
16279             return true;
16280         },
16281
16282         /**
16283          * Iterates over all of the DragDrop elements to find ones we are
16284          * hovering over or dropping on
16285          * @method fireEvents
16286          * @param {Event} e the event
16287          * @param {boolean} isDrop is this a drop op or a mouseover op?
16288          * @private
16289          * @static
16290          */
16291         fireEvents: function(e, isDrop) {
16292             var dc = this.dragCurrent;
16293
16294             // If the user did the mouse up outside of the window, we could
16295             // get here even though we have ended the drag.
16296             if (!dc || dc.isLocked()) {
16297                 return;
16298             }
16299
16300             var pt = e.getPoint();
16301
16302             // cache the previous dragOver array
16303             var oldOvers = [];
16304
16305             var outEvts   = [];
16306             var overEvts  = [];
16307             var dropEvts  = [];
16308             var enterEvts = [];
16309
16310             // Check to see if the object(s) we were hovering over is no longer
16311             // being hovered over so we can fire the onDragOut event
16312             for (var i in this.dragOvers) {
16313
16314                 var ddo = this.dragOvers[i];
16315
16316                 if (! this.isTypeOfDD(ddo)) {
16317                     continue;
16318                 }
16319
16320                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16321                     outEvts.push( ddo );
16322                 }
16323
16324                 oldOvers[i] = true;
16325                 delete this.dragOvers[i];
16326             }
16327
16328             for (var sGroup in dc.groups) {
16329
16330                 if ("string" != typeof sGroup) {
16331                     continue;
16332                 }
16333
16334                 for (i in this.ids[sGroup]) {
16335                     var oDD = this.ids[sGroup][i];
16336                     if (! this.isTypeOfDD(oDD)) {
16337                         continue;
16338                     }
16339
16340                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16341                         if (this.isOverTarget(pt, oDD, this.mode)) {
16342                             // look for drop interactions
16343                             if (isDrop) {
16344                                 dropEvts.push( oDD );
16345                             // look for drag enter and drag over interactions
16346                             } else {
16347
16348                                 // initial drag over: dragEnter fires
16349                                 if (!oldOvers[oDD.id]) {
16350                                     enterEvts.push( oDD );
16351                                 // subsequent drag overs: dragOver fires
16352                                 } else {
16353                                     overEvts.push( oDD );
16354                                 }
16355
16356                                 this.dragOvers[oDD.id] = oDD;
16357                             }
16358                         }
16359                     }
16360                 }
16361             }
16362
16363             if (this.mode) {
16364                 if (outEvts.length) {
16365                     dc.b4DragOut(e, outEvts);
16366                     dc.onDragOut(e, outEvts);
16367                 }
16368
16369                 if (enterEvts.length) {
16370                     dc.onDragEnter(e, enterEvts);
16371                 }
16372
16373                 if (overEvts.length) {
16374                     dc.b4DragOver(e, overEvts);
16375                     dc.onDragOver(e, overEvts);
16376                 }
16377
16378                 if (dropEvts.length) {
16379                     dc.b4DragDrop(e, dropEvts);
16380                     dc.onDragDrop(e, dropEvts);
16381                 }
16382
16383             } else {
16384                 // fire dragout events
16385                 var len = 0;
16386                 for (i=0, len=outEvts.length; i<len; ++i) {
16387                     dc.b4DragOut(e, outEvts[i].id);
16388                     dc.onDragOut(e, outEvts[i].id);
16389                 }
16390
16391                 // fire enter events
16392                 for (i=0,len=enterEvts.length; i<len; ++i) {
16393                     // dc.b4DragEnter(e, oDD.id);
16394                     dc.onDragEnter(e, enterEvts[i].id);
16395                 }
16396
16397                 // fire over events
16398                 for (i=0,len=overEvts.length; i<len; ++i) {
16399                     dc.b4DragOver(e, overEvts[i].id);
16400                     dc.onDragOver(e, overEvts[i].id);
16401                 }
16402
16403                 // fire drop events
16404                 for (i=0, len=dropEvts.length; i<len; ++i) {
16405                     dc.b4DragDrop(e, dropEvts[i].id);
16406                     dc.onDragDrop(e, dropEvts[i].id);
16407                 }
16408
16409             }
16410
16411             // notify about a drop that did not find a target
16412             if (isDrop && !dropEvts.length) {
16413                 dc.onInvalidDrop(e);
16414             }
16415
16416         },
16417
16418         /**
16419          * Helper function for getting the best match from the list of drag
16420          * and drop objects returned by the drag and drop events when we are
16421          * in INTERSECT mode.  It returns either the first object that the
16422          * cursor is over, or the object that has the greatest overlap with
16423          * the dragged element.
16424          * @method getBestMatch
16425          * @param  {DragDrop[]} dds The array of drag and drop objects
16426          * targeted
16427          * @return {DragDrop}       The best single match
16428          * @static
16429          */
16430         getBestMatch: function(dds) {
16431             var winner = null;
16432             // Return null if the input is not what we expect
16433             //if (!dds || !dds.length || dds.length == 0) {
16434                // winner = null;
16435             // If there is only one item, it wins
16436             //} else if (dds.length == 1) {
16437
16438             var len = dds.length;
16439
16440             if (len == 1) {
16441                 winner = dds[0];
16442             } else {
16443                 // Loop through the targeted items
16444                 for (var i=0; i<len; ++i) {
16445                     var dd = dds[i];
16446                     // If the cursor is over the object, it wins.  If the
16447                     // cursor is over multiple matches, the first one we come
16448                     // to wins.
16449                     if (dd.cursorIsOver) {
16450                         winner = dd;
16451                         break;
16452                     // Otherwise the object with the most overlap wins
16453                     } else {
16454                         if (!winner ||
16455                             winner.overlap.getArea() < dd.overlap.getArea()) {
16456                             winner = dd;
16457                         }
16458                     }
16459                 }
16460             }
16461
16462             return winner;
16463         },
16464
16465         /**
16466          * Refreshes the cache of the top-left and bottom-right points of the
16467          * drag and drop objects in the specified group(s).  This is in the
16468          * format that is stored in the drag and drop instance, so typical
16469          * usage is:
16470          * <code>
16471          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16472          * </code>
16473          * Alternatively:
16474          * <code>
16475          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16476          * </code>
16477          * @TODO this really should be an indexed array.  Alternatively this
16478          * method could accept both.
16479          * @method refreshCache
16480          * @param {Object} groups an associative array of groups to refresh
16481          * @static
16482          */
16483         refreshCache: function(groups) {
16484             for (var sGroup in groups) {
16485                 if ("string" != typeof sGroup) {
16486                     continue;
16487                 }
16488                 for (var i in this.ids[sGroup]) {
16489                     var oDD = this.ids[sGroup][i];
16490
16491                     if (this.isTypeOfDD(oDD)) {
16492                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16493                         var loc = this.getLocation(oDD);
16494                         if (loc) {
16495                             this.locationCache[oDD.id] = loc;
16496                         } else {
16497                             delete this.locationCache[oDD.id];
16498                             // this will unregister the drag and drop object if
16499                             // the element is not in a usable state
16500                             // oDD.unreg();
16501                         }
16502                     }
16503                 }
16504             }
16505         },
16506
16507         /**
16508          * This checks to make sure an element exists and is in the DOM.  The
16509          * main purpose is to handle cases where innerHTML is used to remove
16510          * drag and drop objects from the DOM.  IE provides an 'unspecified
16511          * error' when trying to access the offsetParent of such an element
16512          * @method verifyEl
16513          * @param {HTMLElement} el the element to check
16514          * @return {boolean} true if the element looks usable
16515          * @static
16516          */
16517         verifyEl: function(el) {
16518             if (el) {
16519                 var parent;
16520                 if(Roo.isIE){
16521                     try{
16522                         parent = el.offsetParent;
16523                     }catch(e){}
16524                 }else{
16525                     parent = el.offsetParent;
16526                 }
16527                 if (parent) {
16528                     return true;
16529                 }
16530             }
16531
16532             return false;
16533         },
16534
16535         /**
16536          * Returns a Region object containing the drag and drop element's position
16537          * and size, including the padding configured for it
16538          * @method getLocation
16539          * @param {DragDrop} oDD the drag and drop object to get the
16540          *                       location for
16541          * @return {Roo.lib.Region} a Region object representing the total area
16542          *                             the element occupies, including any padding
16543          *                             the instance is configured for.
16544          * @static
16545          */
16546         getLocation: function(oDD) {
16547             if (! this.isTypeOfDD(oDD)) {
16548                 return null;
16549             }
16550
16551             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16552
16553             try {
16554                 pos= Roo.lib.Dom.getXY(el);
16555             } catch (e) { }
16556
16557             if (!pos) {
16558                 return null;
16559             }
16560
16561             x1 = pos[0];
16562             x2 = x1 + el.offsetWidth;
16563             y1 = pos[1];
16564             y2 = y1 + el.offsetHeight;
16565
16566             t = y1 - oDD.padding[0];
16567             r = x2 + oDD.padding[1];
16568             b = y2 + oDD.padding[2];
16569             l = x1 - oDD.padding[3];
16570
16571             return new Roo.lib.Region( t, r, b, l );
16572         },
16573
16574         /**
16575          * Checks the cursor location to see if it over the target
16576          * @method isOverTarget
16577          * @param {Roo.lib.Point} pt The point to evaluate
16578          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16579          * @return {boolean} true if the mouse is over the target
16580          * @private
16581          * @static
16582          */
16583         isOverTarget: function(pt, oTarget, intersect) {
16584             // use cache if available
16585             var loc = this.locationCache[oTarget.id];
16586             if (!loc || !this.useCache) {
16587                 loc = this.getLocation(oTarget);
16588                 this.locationCache[oTarget.id] = loc;
16589
16590             }
16591
16592             if (!loc) {
16593                 return false;
16594             }
16595
16596             oTarget.cursorIsOver = loc.contains( pt );
16597
16598             // DragDrop is using this as a sanity check for the initial mousedown
16599             // in this case we are done.  In POINT mode, if the drag obj has no
16600             // contraints, we are also done. Otherwise we need to evaluate the
16601             // location of the target as related to the actual location of the
16602             // dragged element.
16603             var dc = this.dragCurrent;
16604             if (!dc || !dc.getTargetCoord ||
16605                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16606                 return oTarget.cursorIsOver;
16607             }
16608
16609             oTarget.overlap = null;
16610
16611             // Get the current location of the drag element, this is the
16612             // location of the mouse event less the delta that represents
16613             // where the original mousedown happened on the element.  We
16614             // need to consider constraints and ticks as well.
16615             var pos = dc.getTargetCoord(pt.x, pt.y);
16616
16617             var el = dc.getDragEl();
16618             var curRegion = new Roo.lib.Region( pos.y,
16619                                                    pos.x + el.offsetWidth,
16620                                                    pos.y + el.offsetHeight,
16621                                                    pos.x );
16622
16623             var overlap = curRegion.intersect(loc);
16624
16625             if (overlap) {
16626                 oTarget.overlap = overlap;
16627                 return (intersect) ? true : oTarget.cursorIsOver;
16628             } else {
16629                 return false;
16630             }
16631         },
16632
16633         /**
16634          * unload event handler
16635          * @method _onUnload
16636          * @private
16637          * @static
16638          */
16639         _onUnload: function(e, me) {
16640             Roo.dd.DragDropMgr.unregAll();
16641         },
16642
16643         /**
16644          * Cleans up the drag and drop events and objects.
16645          * @method unregAll
16646          * @private
16647          * @static
16648          */
16649         unregAll: function() {
16650
16651             if (this.dragCurrent) {
16652                 this.stopDrag();
16653                 this.dragCurrent = null;
16654             }
16655
16656             this._execOnAll("unreg", []);
16657
16658             for (i in this.elementCache) {
16659                 delete this.elementCache[i];
16660             }
16661
16662             this.elementCache = {};
16663             this.ids = {};
16664         },
16665
16666         /**
16667          * A cache of DOM elements
16668          * @property elementCache
16669          * @private
16670          * @static
16671          */
16672         elementCache: {},
16673
16674         /**
16675          * Get the wrapper for the DOM element specified
16676          * @method getElWrapper
16677          * @param {String} id the id of the element to get
16678          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16679          * @private
16680          * @deprecated This wrapper isn't that useful
16681          * @static
16682          */
16683         getElWrapper: function(id) {
16684             var oWrapper = this.elementCache[id];
16685             if (!oWrapper || !oWrapper.el) {
16686                 oWrapper = this.elementCache[id] =
16687                     new this.ElementWrapper(Roo.getDom(id));
16688             }
16689             return oWrapper;
16690         },
16691
16692         /**
16693          * Returns the actual DOM element
16694          * @method getElement
16695          * @param {String} id the id of the elment to get
16696          * @return {Object} The element
16697          * @deprecated use Roo.getDom instead
16698          * @static
16699          */
16700         getElement: function(id) {
16701             return Roo.getDom(id);
16702         },
16703
16704         /**
16705          * Returns the style property for the DOM element (i.e.,
16706          * document.getElById(id).style)
16707          * @method getCss
16708          * @param {String} id the id of the elment to get
16709          * @return {Object} The style property of the element
16710          * @deprecated use Roo.getDom instead
16711          * @static
16712          */
16713         getCss: function(id) {
16714             var el = Roo.getDom(id);
16715             return (el) ? el.style : null;
16716         },
16717
16718         /**
16719          * Inner class for cached elements
16720          * @class DragDropMgr.ElementWrapper
16721          * @for DragDropMgr
16722          * @private
16723          * @deprecated
16724          */
16725         ElementWrapper: function(el) {
16726                 /**
16727                  * The element
16728                  * @property el
16729                  */
16730                 this.el = el || null;
16731                 /**
16732                  * The element id
16733                  * @property id
16734                  */
16735                 this.id = this.el && el.id;
16736                 /**
16737                  * A reference to the style property
16738                  * @property css
16739                  */
16740                 this.css = this.el && el.style;
16741             },
16742
16743         /**
16744          * Returns the X position of an html element
16745          * @method getPosX
16746          * @param el the element for which to get the position
16747          * @return {int} the X coordinate
16748          * @for DragDropMgr
16749          * @deprecated use Roo.lib.Dom.getX instead
16750          * @static
16751          */
16752         getPosX: function(el) {
16753             return Roo.lib.Dom.getX(el);
16754         },
16755
16756         /**
16757          * Returns the Y position of an html element
16758          * @method getPosY
16759          * @param el the element for which to get the position
16760          * @return {int} the Y coordinate
16761          * @deprecated use Roo.lib.Dom.getY instead
16762          * @static
16763          */
16764         getPosY: function(el) {
16765             return Roo.lib.Dom.getY(el);
16766         },
16767
16768         /**
16769          * Swap two nodes.  In IE, we use the native method, for others we
16770          * emulate the IE behavior
16771          * @method swapNode
16772          * @param n1 the first node to swap
16773          * @param n2 the other node to swap
16774          * @static
16775          */
16776         swapNode: function(n1, n2) {
16777             if (n1.swapNode) {
16778                 n1.swapNode(n2);
16779             } else {
16780                 var p = n2.parentNode;
16781                 var s = n2.nextSibling;
16782
16783                 if (s == n1) {
16784                     p.insertBefore(n1, n2);
16785                 } else if (n2 == n1.nextSibling) {
16786                     p.insertBefore(n2, n1);
16787                 } else {
16788                     n1.parentNode.replaceChild(n2, n1);
16789                     p.insertBefore(n1, s);
16790                 }
16791             }
16792         },
16793
16794         /**
16795          * Returns the current scroll position
16796          * @method getScroll
16797          * @private
16798          * @static
16799          */
16800         getScroll: function () {
16801             var t, l, dde=document.documentElement, db=document.body;
16802             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16803                 t = dde.scrollTop;
16804                 l = dde.scrollLeft;
16805             } else if (db) {
16806                 t = db.scrollTop;
16807                 l = db.scrollLeft;
16808             } else {
16809
16810             }
16811             return { top: t, left: l };
16812         },
16813
16814         /**
16815          * Returns the specified element style property
16816          * @method getStyle
16817          * @param {HTMLElement} el          the element
16818          * @param {string}      styleProp   the style property
16819          * @return {string} The value of the style property
16820          * @deprecated use Roo.lib.Dom.getStyle
16821          * @static
16822          */
16823         getStyle: function(el, styleProp) {
16824             return Roo.fly(el).getStyle(styleProp);
16825         },
16826
16827         /**
16828          * Gets the scrollTop
16829          * @method getScrollTop
16830          * @return {int} the document's scrollTop
16831          * @static
16832          */
16833         getScrollTop: function () { return this.getScroll().top; },
16834
16835         /**
16836          * Gets the scrollLeft
16837          * @method getScrollLeft
16838          * @return {int} the document's scrollTop
16839          * @static
16840          */
16841         getScrollLeft: function () { return this.getScroll().left; },
16842
16843         /**
16844          * Sets the x/y position of an element to the location of the
16845          * target element.
16846          * @method moveToEl
16847          * @param {HTMLElement} moveEl      The element to move
16848          * @param {HTMLElement} targetEl    The position reference element
16849          * @static
16850          */
16851         moveToEl: function (moveEl, targetEl) {
16852             var aCoord = Roo.lib.Dom.getXY(targetEl);
16853             Roo.lib.Dom.setXY(moveEl, aCoord);
16854         },
16855
16856         /**
16857          * Numeric array sort function
16858          * @method numericSort
16859          * @static
16860          */
16861         numericSort: function(a, b) { return (a - b); },
16862
16863         /**
16864          * Internal counter
16865          * @property _timeoutCount
16866          * @private
16867          * @static
16868          */
16869         _timeoutCount: 0,
16870
16871         /**
16872          * Trying to make the load order less important.  Without this we get
16873          * an error if this file is loaded before the Event Utility.
16874          * @method _addListeners
16875          * @private
16876          * @static
16877          */
16878         _addListeners: function() {
16879             var DDM = Roo.dd.DDM;
16880             if ( Roo.lib.Event && document ) {
16881                 DDM._onLoad();
16882             } else {
16883                 if (DDM._timeoutCount > 2000) {
16884                 } else {
16885                     setTimeout(DDM._addListeners, 10);
16886                     if (document && document.body) {
16887                         DDM._timeoutCount += 1;
16888                     }
16889                 }
16890             }
16891         },
16892
16893         /**
16894          * Recursively searches the immediate parent and all child nodes for
16895          * the handle element in order to determine wheter or not it was
16896          * clicked.
16897          * @method handleWasClicked
16898          * @param node the html element to inspect
16899          * @static
16900          */
16901         handleWasClicked: function(node, id) {
16902             if (this.isHandle(id, node.id)) {
16903                 return true;
16904             } else {
16905                 // check to see if this is a text node child of the one we want
16906                 var p = node.parentNode;
16907
16908                 while (p) {
16909                     if (this.isHandle(id, p.id)) {
16910                         return true;
16911                     } else {
16912                         p = p.parentNode;
16913                     }
16914                 }
16915             }
16916
16917             return false;
16918         }
16919
16920     };
16921
16922 }();
16923
16924 // shorter alias, save a few bytes
16925 Roo.dd.DDM = Roo.dd.DragDropMgr;
16926 Roo.dd.DDM._addListeners();
16927
16928 }/*
16929  * Based on:
16930  * Ext JS Library 1.1.1
16931  * Copyright(c) 2006-2007, Ext JS, LLC.
16932  *
16933  * Originally Released Under LGPL - original licence link has changed is not relivant.
16934  *
16935  * Fork - LGPL
16936  * <script type="text/javascript">
16937  */
16938
16939 /**
16940  * @class Roo.dd.DD
16941  * A DragDrop implementation where the linked element follows the
16942  * mouse cursor during a drag.
16943  * @extends Roo.dd.DragDrop
16944  * @constructor
16945  * @param {String} id the id of the linked element
16946  * @param {String} sGroup the group of related DragDrop items
16947  * @param {object} config an object containing configurable attributes
16948  *                Valid properties for DD:
16949  *                    scroll
16950  */
16951 Roo.dd.DD = function(id, sGroup, config) {
16952     if (id) {
16953         this.init(id, sGroup, config);
16954     }
16955 };
16956
16957 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16958
16959     /**
16960      * When set to true, the utility automatically tries to scroll the browser
16961      * window wehn a drag and drop element is dragged near the viewport boundary.
16962      * Defaults to true.
16963      * @property scroll
16964      * @type boolean
16965      */
16966     scroll: true,
16967
16968     /**
16969      * Sets the pointer offset to the distance between the linked element's top
16970      * left corner and the location the element was clicked
16971      * @method autoOffset
16972      * @param {int} iPageX the X coordinate of the click
16973      * @param {int} iPageY the Y coordinate of the click
16974      */
16975     autoOffset: function(iPageX, iPageY) {
16976         var x = iPageX - this.startPageX;
16977         var y = iPageY - this.startPageY;
16978         this.setDelta(x, y);
16979     },
16980
16981     /**
16982      * Sets the pointer offset.  You can call this directly to force the
16983      * offset to be in a particular location (e.g., pass in 0,0 to set it
16984      * to the center of the object)
16985      * @method setDelta
16986      * @param {int} iDeltaX the distance from the left
16987      * @param {int} iDeltaY the distance from the top
16988      */
16989     setDelta: function(iDeltaX, iDeltaY) {
16990         this.deltaX = iDeltaX;
16991         this.deltaY = iDeltaY;
16992     },
16993
16994     /**
16995      * Sets the drag element to the location of the mousedown or click event,
16996      * maintaining the cursor location relative to the location on the element
16997      * that was clicked.  Override this if you want to place the element in a
16998      * location other than where the cursor is.
16999      * @method setDragElPos
17000      * @param {int} iPageX the X coordinate of the mousedown or drag event
17001      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17002      */
17003     setDragElPos: function(iPageX, iPageY) {
17004         // the first time we do this, we are going to check to make sure
17005         // the element has css positioning
17006
17007         var el = this.getDragEl();
17008         this.alignElWithMouse(el, iPageX, iPageY);
17009     },
17010
17011     /**
17012      * Sets the element to the location of the mousedown or click event,
17013      * maintaining the cursor location relative to the location on the element
17014      * that was clicked.  Override this if you want to place the element in a
17015      * location other than where the cursor is.
17016      * @method alignElWithMouse
17017      * @param {HTMLElement} el the element to move
17018      * @param {int} iPageX the X coordinate of the mousedown or drag event
17019      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17020      */
17021     alignElWithMouse: function(el, iPageX, iPageY) {
17022         var oCoord = this.getTargetCoord(iPageX, iPageY);
17023         var fly = el.dom ? el : Roo.fly(el);
17024         if (!this.deltaSetXY) {
17025             var aCoord = [oCoord.x, oCoord.y];
17026             fly.setXY(aCoord);
17027             var newLeft = fly.getLeft(true);
17028             var newTop  = fly.getTop(true);
17029             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17030         } else {
17031             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17032         }
17033
17034         this.cachePosition(oCoord.x, oCoord.y);
17035         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17036         return oCoord;
17037     },
17038
17039     /**
17040      * Saves the most recent position so that we can reset the constraints and
17041      * tick marks on-demand.  We need to know this so that we can calculate the
17042      * number of pixels the element is offset from its original position.
17043      * @method cachePosition
17044      * @param iPageX the current x position (optional, this just makes it so we
17045      * don't have to look it up again)
17046      * @param iPageY the current y position (optional, this just makes it so we
17047      * don't have to look it up again)
17048      */
17049     cachePosition: function(iPageX, iPageY) {
17050         if (iPageX) {
17051             this.lastPageX = iPageX;
17052             this.lastPageY = iPageY;
17053         } else {
17054             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17055             this.lastPageX = aCoord[0];
17056             this.lastPageY = aCoord[1];
17057         }
17058     },
17059
17060     /**
17061      * Auto-scroll the window if the dragged object has been moved beyond the
17062      * visible window boundary.
17063      * @method autoScroll
17064      * @param {int} x the drag element's x position
17065      * @param {int} y the drag element's y position
17066      * @param {int} h the height of the drag element
17067      * @param {int} w the width of the drag element
17068      * @private
17069      */
17070     autoScroll: function(x, y, h, w) {
17071
17072         if (this.scroll) {
17073             // The client height
17074             var clientH = Roo.lib.Dom.getViewWidth();
17075
17076             // The client width
17077             var clientW = Roo.lib.Dom.getViewHeight();
17078
17079             // The amt scrolled down
17080             var st = this.DDM.getScrollTop();
17081
17082             // The amt scrolled right
17083             var sl = this.DDM.getScrollLeft();
17084
17085             // Location of the bottom of the element
17086             var bot = h + y;
17087
17088             // Location of the right of the element
17089             var right = w + x;
17090
17091             // The distance from the cursor to the bottom of the visible area,
17092             // adjusted so that we don't scroll if the cursor is beyond the
17093             // element drag constraints
17094             var toBot = (clientH + st - y - this.deltaY);
17095
17096             // The distance from the cursor to the right of the visible area
17097             var toRight = (clientW + sl - x - this.deltaX);
17098
17099
17100             // How close to the edge the cursor must be before we scroll
17101             // var thresh = (document.all) ? 100 : 40;
17102             var thresh = 40;
17103
17104             // How many pixels to scroll per autoscroll op.  This helps to reduce
17105             // clunky scrolling. IE is more sensitive about this ... it needs this
17106             // value to be higher.
17107             var scrAmt = (document.all) ? 80 : 30;
17108
17109             // Scroll down if we are near the bottom of the visible page and the
17110             // obj extends below the crease
17111             if ( bot > clientH && toBot < thresh ) {
17112                 window.scrollTo(sl, st + scrAmt);
17113             }
17114
17115             // Scroll up if the window is scrolled down and the top of the object
17116             // goes above the top border
17117             if ( y < st && st > 0 && y - st < thresh ) {
17118                 window.scrollTo(sl, st - scrAmt);
17119             }
17120
17121             // Scroll right if the obj is beyond the right border and the cursor is
17122             // near the border.
17123             if ( right > clientW && toRight < thresh ) {
17124                 window.scrollTo(sl + scrAmt, st);
17125             }
17126
17127             // Scroll left if the window has been scrolled to the right and the obj
17128             // extends past the left border
17129             if ( x < sl && sl > 0 && x - sl < thresh ) {
17130                 window.scrollTo(sl - scrAmt, st);
17131             }
17132         }
17133     },
17134
17135     /**
17136      * Finds the location the element should be placed if we want to move
17137      * it to where the mouse location less the click offset would place us.
17138      * @method getTargetCoord
17139      * @param {int} iPageX the X coordinate of the click
17140      * @param {int} iPageY the Y coordinate of the click
17141      * @return an object that contains the coordinates (Object.x and Object.y)
17142      * @private
17143      */
17144     getTargetCoord: function(iPageX, iPageY) {
17145
17146
17147         var x = iPageX - this.deltaX;
17148         var y = iPageY - this.deltaY;
17149
17150         if (this.constrainX) {
17151             if (x < this.minX) { x = this.minX; }
17152             if (x > this.maxX) { x = this.maxX; }
17153         }
17154
17155         if (this.constrainY) {
17156             if (y < this.minY) { y = this.minY; }
17157             if (y > this.maxY) { y = this.maxY; }
17158         }
17159
17160         x = this.getTick(x, this.xTicks);
17161         y = this.getTick(y, this.yTicks);
17162
17163
17164         return {x:x, y:y};
17165     },
17166
17167     /*
17168      * Sets up config options specific to this class. Overrides
17169      * Roo.dd.DragDrop, but all versions of this method through the
17170      * inheritance chain are called
17171      */
17172     applyConfig: function() {
17173         Roo.dd.DD.superclass.applyConfig.call(this);
17174         this.scroll = (this.config.scroll !== false);
17175     },
17176
17177     /*
17178      * Event that fires prior to the onMouseDown event.  Overrides
17179      * Roo.dd.DragDrop.
17180      */
17181     b4MouseDown: function(e) {
17182         // this.resetConstraints();
17183         this.autoOffset(e.getPageX(),
17184                             e.getPageY());
17185     },
17186
17187     /*
17188      * Event that fires prior to the onDrag event.  Overrides
17189      * Roo.dd.DragDrop.
17190      */
17191     b4Drag: function(e) {
17192         this.setDragElPos(e.getPageX(),
17193                             e.getPageY());
17194     },
17195
17196     toString: function() {
17197         return ("DD " + this.id);
17198     }
17199
17200     //////////////////////////////////////////////////////////////////////////
17201     // Debugging ygDragDrop events that can be overridden
17202     //////////////////////////////////////////////////////////////////////////
17203     /*
17204     startDrag: function(x, y) {
17205     },
17206
17207     onDrag: function(e) {
17208     },
17209
17210     onDragEnter: function(e, id) {
17211     },
17212
17213     onDragOver: function(e, id) {
17214     },
17215
17216     onDragOut: function(e, id) {
17217     },
17218
17219     onDragDrop: function(e, id) {
17220     },
17221
17222     endDrag: function(e) {
17223     }
17224
17225     */
17226
17227 });/*
17228  * Based on:
17229  * Ext JS Library 1.1.1
17230  * Copyright(c) 2006-2007, Ext JS, LLC.
17231  *
17232  * Originally Released Under LGPL - original licence link has changed is not relivant.
17233  *
17234  * Fork - LGPL
17235  * <script type="text/javascript">
17236  */
17237
17238 /**
17239  * @class Roo.dd.DDProxy
17240  * A DragDrop implementation that inserts an empty, bordered div into
17241  * the document that follows the cursor during drag operations.  At the time of
17242  * the click, the frame div is resized to the dimensions of the linked html
17243  * element, and moved to the exact location of the linked element.
17244  *
17245  * References to the "frame" element refer to the single proxy element that
17246  * was created to be dragged in place of all DDProxy elements on the
17247  * page.
17248  *
17249  * @extends Roo.dd.DD
17250  * @constructor
17251  * @param {String} id the id of the linked html element
17252  * @param {String} sGroup the group of related DragDrop objects
17253  * @param {object} config an object containing configurable attributes
17254  *                Valid properties for DDProxy in addition to those in DragDrop:
17255  *                   resizeFrame, centerFrame, dragElId
17256  */
17257 Roo.dd.DDProxy = function(id, sGroup, config) {
17258     if (id) {
17259         this.init(id, sGroup, config);
17260         this.initFrame();
17261     }
17262 };
17263
17264 /**
17265  * The default drag frame div id
17266  * @property Roo.dd.DDProxy.dragElId
17267  * @type String
17268  * @static
17269  */
17270 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17271
17272 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17273
17274     /**
17275      * By default we resize the drag frame to be the same size as the element
17276      * we want to drag (this is to get the frame effect).  We can turn it off
17277      * if we want a different behavior.
17278      * @property resizeFrame
17279      * @type boolean
17280      */
17281     resizeFrame: true,
17282
17283     /**
17284      * By default the frame is positioned exactly where the drag element is, so
17285      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17286      * you do not have constraints on the obj is to have the drag frame centered
17287      * around the cursor.  Set centerFrame to true for this effect.
17288      * @property centerFrame
17289      * @type boolean
17290      */
17291     centerFrame: false,
17292
17293     /**
17294      * Creates the proxy element if it does not yet exist
17295      * @method createFrame
17296      */
17297     createFrame: function() {
17298         var self = this;
17299         var body = document.body;
17300
17301         if (!body || !body.firstChild) {
17302             setTimeout( function() { self.createFrame(); }, 50 );
17303             return;
17304         }
17305
17306         var div = this.getDragEl();
17307
17308         if (!div) {
17309             div    = document.createElement("div");
17310             div.id = this.dragElId;
17311             var s  = div.style;
17312
17313             s.position   = "absolute";
17314             s.visibility = "hidden";
17315             s.cursor     = "move";
17316             s.border     = "2px solid #aaa";
17317             s.zIndex     = 999;
17318
17319             // appendChild can blow up IE if invoked prior to the window load event
17320             // while rendering a table.  It is possible there are other scenarios
17321             // that would cause this to happen as well.
17322             body.insertBefore(div, body.firstChild);
17323         }
17324     },
17325
17326     /**
17327      * Initialization for the drag frame element.  Must be called in the
17328      * constructor of all subclasses
17329      * @method initFrame
17330      */
17331     initFrame: function() {
17332         this.createFrame();
17333     },
17334
17335     applyConfig: function() {
17336         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17337
17338         this.resizeFrame = (this.config.resizeFrame !== false);
17339         this.centerFrame = (this.config.centerFrame);
17340         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17341     },
17342
17343     /**
17344      * Resizes the drag frame to the dimensions of the clicked object, positions
17345      * it over the object, and finally displays it
17346      * @method showFrame
17347      * @param {int} iPageX X click position
17348      * @param {int} iPageY Y click position
17349      * @private
17350      */
17351     showFrame: function(iPageX, iPageY) {
17352         var el = this.getEl();
17353         var dragEl = this.getDragEl();
17354         var s = dragEl.style;
17355
17356         this._resizeProxy();
17357
17358         if (this.centerFrame) {
17359             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17360                            Math.round(parseInt(s.height, 10)/2) );
17361         }
17362
17363         this.setDragElPos(iPageX, iPageY);
17364
17365         Roo.fly(dragEl).show();
17366     },
17367
17368     /**
17369      * The proxy is automatically resized to the dimensions of the linked
17370      * element when a drag is initiated, unless resizeFrame is set to false
17371      * @method _resizeProxy
17372      * @private
17373      */
17374     _resizeProxy: function() {
17375         if (this.resizeFrame) {
17376             var el = this.getEl();
17377             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17378         }
17379     },
17380
17381     // overrides Roo.dd.DragDrop
17382     b4MouseDown: function(e) {
17383         var x = e.getPageX();
17384         var y = e.getPageY();
17385         this.autoOffset(x, y);
17386         this.setDragElPos(x, y);
17387     },
17388
17389     // overrides Roo.dd.DragDrop
17390     b4StartDrag: function(x, y) {
17391         // show the drag frame
17392         this.showFrame(x, y);
17393     },
17394
17395     // overrides Roo.dd.DragDrop
17396     b4EndDrag: function(e) {
17397         Roo.fly(this.getDragEl()).hide();
17398     },
17399
17400     // overrides Roo.dd.DragDrop
17401     // By default we try to move the element to the last location of the frame.
17402     // This is so that the default behavior mirrors that of Roo.dd.DD.
17403     endDrag: function(e) {
17404
17405         var lel = this.getEl();
17406         var del = this.getDragEl();
17407
17408         // Show the drag frame briefly so we can get its position
17409         del.style.visibility = "";
17410
17411         this.beforeMove();
17412         // Hide the linked element before the move to get around a Safari
17413         // rendering bug.
17414         lel.style.visibility = "hidden";
17415         Roo.dd.DDM.moveToEl(lel, del);
17416         del.style.visibility = "hidden";
17417         lel.style.visibility = "";
17418
17419         this.afterDrag();
17420     },
17421
17422     beforeMove : function(){
17423
17424     },
17425
17426     afterDrag : function(){
17427
17428     },
17429
17430     toString: function() {
17431         return ("DDProxy " + this.id);
17432     }
17433
17434 });
17435 /*
17436  * Based on:
17437  * Ext JS Library 1.1.1
17438  * Copyright(c) 2006-2007, Ext JS, LLC.
17439  *
17440  * Originally Released Under LGPL - original licence link has changed is not relivant.
17441  *
17442  * Fork - LGPL
17443  * <script type="text/javascript">
17444  */
17445
17446  /**
17447  * @class Roo.dd.DDTarget
17448  * A DragDrop implementation that does not move, but can be a drop
17449  * target.  You would get the same result by simply omitting implementation
17450  * for the event callbacks, but this way we reduce the processing cost of the
17451  * event listener and the callbacks.
17452  * @extends Roo.dd.DragDrop
17453  * @constructor
17454  * @param {String} id the id of the element that is a drop target
17455  * @param {String} sGroup the group of related DragDrop objects
17456  * @param {object} config an object containing configurable attributes
17457  *                 Valid properties for DDTarget in addition to those in
17458  *                 DragDrop:
17459  *                    none
17460  */
17461 Roo.dd.DDTarget = function(id, sGroup, config) {
17462     if (id) {
17463         this.initTarget(id, sGroup, config);
17464     }
17465     if (config.listeners || config.events) { 
17466        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17467             listeners : config.listeners || {}, 
17468             events : config.events || {} 
17469         });    
17470     }
17471 };
17472
17473 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17474 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17475     toString: function() {
17476         return ("DDTarget " + this.id);
17477     }
17478 });
17479 /*
17480  * Based on:
17481  * Ext JS Library 1.1.1
17482  * Copyright(c) 2006-2007, Ext JS, LLC.
17483  *
17484  * Originally Released Under LGPL - original licence link has changed is not relivant.
17485  *
17486  * Fork - LGPL
17487  * <script type="text/javascript">
17488  */
17489  
17490
17491 /**
17492  * @class Roo.dd.ScrollManager
17493  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17494  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17495  * @singleton
17496  */
17497 Roo.dd.ScrollManager = function(){
17498     var ddm = Roo.dd.DragDropMgr;
17499     var els = {};
17500     var dragEl = null;
17501     var proc = {};
17502     
17503     var onStop = function(e){
17504         dragEl = null;
17505         clearProc();
17506     };
17507     
17508     var triggerRefresh = function(){
17509         if(ddm.dragCurrent){
17510              ddm.refreshCache(ddm.dragCurrent.groups);
17511         }
17512     };
17513     
17514     var doScroll = function(){
17515         if(ddm.dragCurrent){
17516             var dds = Roo.dd.ScrollManager;
17517             if(!dds.animate){
17518                 if(proc.el.scroll(proc.dir, dds.increment)){
17519                     triggerRefresh();
17520                 }
17521             }else{
17522                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17523             }
17524         }
17525     };
17526     
17527     var clearProc = function(){
17528         if(proc.id){
17529             clearInterval(proc.id);
17530         }
17531         proc.id = 0;
17532         proc.el = null;
17533         proc.dir = "";
17534     };
17535     
17536     var startProc = function(el, dir){
17537         clearProc();
17538         proc.el = el;
17539         proc.dir = dir;
17540         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17541     };
17542     
17543     var onFire = function(e, isDrop){
17544         if(isDrop || !ddm.dragCurrent){ return; }
17545         var dds = Roo.dd.ScrollManager;
17546         if(!dragEl || dragEl != ddm.dragCurrent){
17547             dragEl = ddm.dragCurrent;
17548             // refresh regions on drag start
17549             dds.refreshCache();
17550         }
17551         
17552         var xy = Roo.lib.Event.getXY(e);
17553         var pt = new Roo.lib.Point(xy[0], xy[1]);
17554         for(var id in els){
17555             var el = els[id], r = el._region;
17556             if(r && r.contains(pt) && el.isScrollable()){
17557                 if(r.bottom - pt.y <= dds.thresh){
17558                     if(proc.el != el){
17559                         startProc(el, "down");
17560                     }
17561                     return;
17562                 }else if(r.right - pt.x <= dds.thresh){
17563                     if(proc.el != el){
17564                         startProc(el, "left");
17565                     }
17566                     return;
17567                 }else if(pt.y - r.top <= dds.thresh){
17568                     if(proc.el != el){
17569                         startProc(el, "up");
17570                     }
17571                     return;
17572                 }else if(pt.x - r.left <= dds.thresh){
17573                     if(proc.el != el){
17574                         startProc(el, "right");
17575                     }
17576                     return;
17577                 }
17578             }
17579         }
17580         clearProc();
17581     };
17582     
17583     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17584     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17585     
17586     return {
17587         /**
17588          * Registers new overflow element(s) to auto scroll
17589          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17590          */
17591         register : function(el){
17592             if(el instanceof Array){
17593                 for(var i = 0, len = el.length; i < len; i++) {
17594                         this.register(el[i]);
17595                 }
17596             }else{
17597                 el = Roo.get(el);
17598                 els[el.id] = el;
17599             }
17600         },
17601         
17602         /**
17603          * Unregisters overflow element(s) so they are no longer scrolled
17604          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17605          */
17606         unregister : function(el){
17607             if(el instanceof Array){
17608                 for(var i = 0, len = el.length; i < len; i++) {
17609                         this.unregister(el[i]);
17610                 }
17611             }else{
17612                 el = Roo.get(el);
17613                 delete els[el.id];
17614             }
17615         },
17616         
17617         /**
17618          * The number of pixels from the edge of a container the pointer needs to be to 
17619          * trigger scrolling (defaults to 25)
17620          * @type Number
17621          */
17622         thresh : 25,
17623         
17624         /**
17625          * The number of pixels to scroll in each scroll increment (defaults to 50)
17626          * @type Number
17627          */
17628         increment : 100,
17629         
17630         /**
17631          * The frequency of scrolls in milliseconds (defaults to 500)
17632          * @type Number
17633          */
17634         frequency : 500,
17635         
17636         /**
17637          * True to animate the scroll (defaults to true)
17638          * @type Boolean
17639          */
17640         animate: true,
17641         
17642         /**
17643          * The animation duration in seconds - 
17644          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17645          * @type Number
17646          */
17647         animDuration: .4,
17648         
17649         /**
17650          * Manually trigger a cache refresh.
17651          */
17652         refreshCache : function(){
17653             for(var id in els){
17654                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17655                     els[id]._region = els[id].getRegion();
17656                 }
17657             }
17658         }
17659     };
17660 }();/*
17661  * Based on:
17662  * Ext JS Library 1.1.1
17663  * Copyright(c) 2006-2007, Ext JS, LLC.
17664  *
17665  * Originally Released Under LGPL - original licence link has changed is not relivant.
17666  *
17667  * Fork - LGPL
17668  * <script type="text/javascript">
17669  */
17670  
17671
17672 /**
17673  * @class Roo.dd.Registry
17674  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17675  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17676  * @singleton
17677  */
17678 Roo.dd.Registry = function(){
17679     var elements = {}; 
17680     var handles = {}; 
17681     var autoIdSeed = 0;
17682
17683     var getId = function(el, autogen){
17684         if(typeof el == "string"){
17685             return el;
17686         }
17687         var id = el.id;
17688         if(!id && autogen !== false){
17689             id = "roodd-" + (++autoIdSeed);
17690             el.id = id;
17691         }
17692         return id;
17693     };
17694     
17695     return {
17696     /**
17697      * Register a drag drop element
17698      * @param {String|HTMLElement} element The id or DOM node to register
17699      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17700      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17701      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17702      * populated in the data object (if applicable):
17703      * <pre>
17704 Value      Description<br />
17705 ---------  ------------------------------------------<br />
17706 handles    Array of DOM nodes that trigger dragging<br />
17707            for the element being registered<br />
17708 isHandle   True if the element passed in triggers<br />
17709            dragging itself, else false
17710 </pre>
17711      */
17712         register : function(el, data){
17713             data = data || {};
17714             if(typeof el == "string"){
17715                 el = document.getElementById(el);
17716             }
17717             data.ddel = el;
17718             elements[getId(el)] = data;
17719             if(data.isHandle !== false){
17720                 handles[data.ddel.id] = data;
17721             }
17722             if(data.handles){
17723                 var hs = data.handles;
17724                 for(var i = 0, len = hs.length; i < len; i++){
17725                         handles[getId(hs[i])] = data;
17726                 }
17727             }
17728         },
17729
17730     /**
17731      * Unregister a drag drop element
17732      * @param {String|HTMLElement}  element The id or DOM node to unregister
17733      */
17734         unregister : function(el){
17735             var id = getId(el, false);
17736             var data = elements[id];
17737             if(data){
17738                 delete elements[id];
17739                 if(data.handles){
17740                     var hs = data.handles;
17741                     for(var i = 0, len = hs.length; i < len; i++){
17742                         delete handles[getId(hs[i], false)];
17743                     }
17744                 }
17745             }
17746         },
17747
17748     /**
17749      * Returns the handle registered for a DOM Node by id
17750      * @param {String|HTMLElement} id The DOM node or id to look up
17751      * @return {Object} handle The custom handle data
17752      */
17753         getHandle : function(id){
17754             if(typeof id != "string"){ // must be element?
17755                 id = id.id;
17756             }
17757             return handles[id];
17758         },
17759
17760     /**
17761      * Returns the handle that is registered for the DOM node that is the target of the event
17762      * @param {Event} e The event
17763      * @return {Object} handle The custom handle data
17764      */
17765         getHandleFromEvent : function(e){
17766             var t = Roo.lib.Event.getTarget(e);
17767             return t ? handles[t.id] : null;
17768         },
17769
17770     /**
17771      * Returns a custom data object that is registered for a DOM node by id
17772      * @param {String|HTMLElement} id The DOM node or id to look up
17773      * @return {Object} data The custom data
17774      */
17775         getTarget : function(id){
17776             if(typeof id != "string"){ // must be element?
17777                 id = id.id;
17778             }
17779             return elements[id];
17780         },
17781
17782     /**
17783      * Returns a custom data object that is registered for the DOM node that is the target of the event
17784      * @param {Event} e The event
17785      * @return {Object} data The custom data
17786      */
17787         getTargetFromEvent : function(e){
17788             var t = Roo.lib.Event.getTarget(e);
17789             return t ? elements[t.id] || handles[t.id] : null;
17790         }
17791     };
17792 }();/*
17793  * Based on:
17794  * Ext JS Library 1.1.1
17795  * Copyright(c) 2006-2007, Ext JS, LLC.
17796  *
17797  * Originally Released Under LGPL - original licence link has changed is not relivant.
17798  *
17799  * Fork - LGPL
17800  * <script type="text/javascript">
17801  */
17802  
17803
17804 /**
17805  * @class Roo.dd.StatusProxy
17806  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17807  * default drag proxy used by all Roo.dd components.
17808  * @constructor
17809  * @param {Object} config
17810  */
17811 Roo.dd.StatusProxy = function(config){
17812     Roo.apply(this, config);
17813     this.id = this.id || Roo.id();
17814     this.el = new Roo.Layer({
17815         dh: {
17816             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17817                 {tag: "div", cls: "x-dd-drop-icon"},
17818                 {tag: "div", cls: "x-dd-drag-ghost"}
17819             ]
17820         }, 
17821         shadow: !config || config.shadow !== false
17822     });
17823     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17824     this.dropStatus = this.dropNotAllowed;
17825 };
17826
17827 Roo.dd.StatusProxy.prototype = {
17828     /**
17829      * @cfg {String} dropAllowed
17830      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17831      */
17832     dropAllowed : "x-dd-drop-ok",
17833     /**
17834      * @cfg {String} dropNotAllowed
17835      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17836      */
17837     dropNotAllowed : "x-dd-drop-nodrop",
17838
17839     /**
17840      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17841      * over the current target element.
17842      * @param {String} cssClass The css class for the new drop status indicator image
17843      */
17844     setStatus : function(cssClass){
17845         cssClass = cssClass || this.dropNotAllowed;
17846         if(this.dropStatus != cssClass){
17847             this.el.replaceClass(this.dropStatus, cssClass);
17848             this.dropStatus = cssClass;
17849         }
17850     },
17851
17852     /**
17853      * Resets the status indicator to the default dropNotAllowed value
17854      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17855      */
17856     reset : function(clearGhost){
17857         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17858         this.dropStatus = this.dropNotAllowed;
17859         if(clearGhost){
17860             this.ghost.update("");
17861         }
17862     },
17863
17864     /**
17865      * Updates the contents of the ghost element
17866      * @param {String} html The html that will replace the current innerHTML of the ghost element
17867      */
17868     update : function(html){
17869         if(typeof html == "string"){
17870             this.ghost.update(html);
17871         }else{
17872             this.ghost.update("");
17873             html.style.margin = "0";
17874             this.ghost.dom.appendChild(html);
17875         }
17876         // ensure float = none set?? cant remember why though.
17877         var el = this.ghost.dom.firstChild;
17878                 if(el){
17879                         Roo.fly(el).setStyle('float', 'none');
17880                 }
17881     },
17882     
17883     /**
17884      * Returns the underlying proxy {@link Roo.Layer}
17885      * @return {Roo.Layer} el
17886     */
17887     getEl : function(){
17888         return this.el;
17889     },
17890
17891     /**
17892      * Returns the ghost element
17893      * @return {Roo.Element} el
17894      */
17895     getGhost : function(){
17896         return this.ghost;
17897     },
17898
17899     /**
17900      * Hides the proxy
17901      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17902      */
17903     hide : function(clear){
17904         this.el.hide();
17905         if(clear){
17906             this.reset(true);
17907         }
17908     },
17909
17910     /**
17911      * Stops the repair animation if it's currently running
17912      */
17913     stop : function(){
17914         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17915             this.anim.stop();
17916         }
17917     },
17918
17919     /**
17920      * Displays this proxy
17921      */
17922     show : function(){
17923         this.el.show();
17924     },
17925
17926     /**
17927      * Force the Layer to sync its shadow and shim positions to the element
17928      */
17929     sync : function(){
17930         this.el.sync();
17931     },
17932
17933     /**
17934      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17935      * invalid drop operation by the item being dragged.
17936      * @param {Array} xy The XY position of the element ([x, y])
17937      * @param {Function} callback The function to call after the repair is complete
17938      * @param {Object} scope The scope in which to execute the callback
17939      */
17940     repair : function(xy, callback, scope){
17941         this.callback = callback;
17942         this.scope = scope;
17943         if(xy && this.animRepair !== false){
17944             this.el.addClass("x-dd-drag-repair");
17945             this.el.hideUnders(true);
17946             this.anim = this.el.shift({
17947                 duration: this.repairDuration || .5,
17948                 easing: 'easeOut',
17949                 xy: xy,
17950                 stopFx: true,
17951                 callback: this.afterRepair,
17952                 scope: this
17953             });
17954         }else{
17955             this.afterRepair();
17956         }
17957     },
17958
17959     // private
17960     afterRepair : function(){
17961         this.hide(true);
17962         if(typeof this.callback == "function"){
17963             this.callback.call(this.scope || this);
17964         }
17965         this.callback = null;
17966         this.scope = null;
17967     }
17968 };/*
17969  * Based on:
17970  * Ext JS Library 1.1.1
17971  * Copyright(c) 2006-2007, Ext JS, LLC.
17972  *
17973  * Originally Released Under LGPL - original licence link has changed is not relivant.
17974  *
17975  * Fork - LGPL
17976  * <script type="text/javascript">
17977  */
17978
17979 /**
17980  * @class Roo.dd.DragSource
17981  * @extends Roo.dd.DDProxy
17982  * A simple class that provides the basic implementation needed to make any element draggable.
17983  * @constructor
17984  * @param {String/HTMLElement/Element} el The container element
17985  * @param {Object} config
17986  */
17987 Roo.dd.DragSource = function(el, config){
17988     this.el = Roo.get(el);
17989     this.dragData = {};
17990     
17991     Roo.apply(this, config);
17992     
17993     if(!this.proxy){
17994         this.proxy = new Roo.dd.StatusProxy();
17995     }
17996
17997     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17998           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17999     
18000     this.dragging = false;
18001 };
18002
18003 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18004     /**
18005      * @cfg {String} dropAllowed
18006      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18007      */
18008     dropAllowed : "x-dd-drop-ok",
18009     /**
18010      * @cfg {String} dropNotAllowed
18011      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18012      */
18013     dropNotAllowed : "x-dd-drop-nodrop",
18014
18015     /**
18016      * Returns the data object associated with this drag source
18017      * @return {Object} data An object containing arbitrary data
18018      */
18019     getDragData : function(e){
18020         return this.dragData;
18021     },
18022
18023     // private
18024     onDragEnter : function(e, id){
18025         var target = Roo.dd.DragDropMgr.getDDById(id);
18026         this.cachedTarget = target;
18027         if(this.beforeDragEnter(target, e, id) !== false){
18028             if(target.isNotifyTarget){
18029                 var status = target.notifyEnter(this, e, this.dragData);
18030                 this.proxy.setStatus(status);
18031             }else{
18032                 this.proxy.setStatus(this.dropAllowed);
18033             }
18034             
18035             if(this.afterDragEnter){
18036                 /**
18037                  * An empty function by default, but provided so that you can perform a custom action
18038                  * when the dragged item enters the drop target by providing an implementation.
18039                  * @param {Roo.dd.DragDrop} target The drop target
18040                  * @param {Event} e The event object
18041                  * @param {String} id The id of the dragged element
18042                  * @method afterDragEnter
18043                  */
18044                 this.afterDragEnter(target, e, id);
18045             }
18046         }
18047     },
18048
18049     /**
18050      * An empty function by default, but provided so that you can perform a custom action
18051      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18052      * @param {Roo.dd.DragDrop} target The drop target
18053      * @param {Event} e The event object
18054      * @param {String} id The id of the dragged element
18055      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18056      */
18057     beforeDragEnter : function(target, e, id){
18058         return true;
18059     },
18060
18061     // private
18062     alignElWithMouse: function() {
18063         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18064         this.proxy.sync();
18065     },
18066
18067     // private
18068     onDragOver : function(e, id){
18069         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18070         if(this.beforeDragOver(target, e, id) !== false){
18071             if(target.isNotifyTarget){
18072                 var status = target.notifyOver(this, e, this.dragData);
18073                 this.proxy.setStatus(status);
18074             }
18075
18076             if(this.afterDragOver){
18077                 /**
18078                  * An empty function by default, but provided so that you can perform a custom action
18079                  * while the dragged item is over the drop target by providing an implementation.
18080                  * @param {Roo.dd.DragDrop} target The drop target
18081                  * @param {Event} e The event object
18082                  * @param {String} id The id of the dragged element
18083                  * @method afterDragOver
18084                  */
18085                 this.afterDragOver(target, e, id);
18086             }
18087         }
18088     },
18089
18090     /**
18091      * An empty function by default, but provided so that you can perform a custom action
18092      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18093      * @param {Roo.dd.DragDrop} target The drop target
18094      * @param {Event} e The event object
18095      * @param {String} id The id of the dragged element
18096      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18097      */
18098     beforeDragOver : function(target, e, id){
18099         return true;
18100     },
18101
18102     // private
18103     onDragOut : function(e, id){
18104         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18105         if(this.beforeDragOut(target, e, id) !== false){
18106             if(target.isNotifyTarget){
18107                 target.notifyOut(this, e, this.dragData);
18108             }
18109             this.proxy.reset();
18110             if(this.afterDragOut){
18111                 /**
18112                  * An empty function by default, but provided so that you can perform a custom action
18113                  * after the dragged item is dragged out of the target without dropping.
18114                  * @param {Roo.dd.DragDrop} target The drop target
18115                  * @param {Event} e The event object
18116                  * @param {String} id The id of the dragged element
18117                  * @method afterDragOut
18118                  */
18119                 this.afterDragOut(target, e, id);
18120             }
18121         }
18122         this.cachedTarget = null;
18123     },
18124
18125     /**
18126      * An empty function by default, but provided so that you can perform a custom action before the dragged
18127      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18128      * @param {Roo.dd.DragDrop} target The drop target
18129      * @param {Event} e The event object
18130      * @param {String} id The id of the dragged element
18131      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18132      */
18133     beforeDragOut : function(target, e, id){
18134         return true;
18135     },
18136     
18137     // private
18138     onDragDrop : function(e, id){
18139         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18140         if(this.beforeDragDrop(target, e, id) !== false){
18141             if(target.isNotifyTarget){
18142                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18143                     this.onValidDrop(target, e, id);
18144                 }else{
18145                     this.onInvalidDrop(target, e, id);
18146                 }
18147             }else{
18148                 this.onValidDrop(target, e, id);
18149             }
18150             
18151             if(this.afterDragDrop){
18152                 /**
18153                  * An empty function by default, but provided so that you can perform a custom action
18154                  * after a valid drag drop has occurred by providing an implementation.
18155                  * @param {Roo.dd.DragDrop} target The drop target
18156                  * @param {Event} e The event object
18157                  * @param {String} id The id of the dropped element
18158                  * @method afterDragDrop
18159                  */
18160                 this.afterDragDrop(target, e, id);
18161             }
18162         }
18163         delete this.cachedTarget;
18164     },
18165
18166     /**
18167      * An empty function by default, but provided so that you can perform a custom action before the dragged
18168      * item is dropped onto the target and optionally cancel the onDragDrop.
18169      * @param {Roo.dd.DragDrop} target The drop target
18170      * @param {Event} e The event object
18171      * @param {String} id The id of the dragged element
18172      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18173      */
18174     beforeDragDrop : function(target, e, id){
18175         return true;
18176     },
18177
18178     // private
18179     onValidDrop : function(target, e, id){
18180         this.hideProxy();
18181         if(this.afterValidDrop){
18182             /**
18183              * An empty function by default, but provided so that you can perform a custom action
18184              * after a valid drop has occurred by providing an implementation.
18185              * @param {Object} target The target DD 
18186              * @param {Event} e The event object
18187              * @param {String} id The id of the dropped element
18188              * @method afterInvalidDrop
18189              */
18190             this.afterValidDrop(target, e, id);
18191         }
18192     },
18193
18194     // private
18195     getRepairXY : function(e, data){
18196         return this.el.getXY();  
18197     },
18198
18199     // private
18200     onInvalidDrop : function(target, e, id){
18201         this.beforeInvalidDrop(target, e, id);
18202         if(this.cachedTarget){
18203             if(this.cachedTarget.isNotifyTarget){
18204                 this.cachedTarget.notifyOut(this, e, this.dragData);
18205             }
18206             this.cacheTarget = null;
18207         }
18208         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18209
18210         if(this.afterInvalidDrop){
18211             /**
18212              * An empty function by default, but provided so that you can perform a custom action
18213              * after an invalid drop has occurred by providing an implementation.
18214              * @param {Event} e The event object
18215              * @param {String} id The id of the dropped element
18216              * @method afterInvalidDrop
18217              */
18218             this.afterInvalidDrop(e, id);
18219         }
18220     },
18221
18222     // private
18223     afterRepair : function(){
18224         if(Roo.enableFx){
18225             this.el.highlight(this.hlColor || "c3daf9");
18226         }
18227         this.dragging = false;
18228     },
18229
18230     /**
18231      * An empty function by default, but provided so that you can perform a custom action after an invalid
18232      * drop has occurred.
18233      * @param {Roo.dd.DragDrop} target The drop target
18234      * @param {Event} e The event object
18235      * @param {String} id The id of the dragged element
18236      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18237      */
18238     beforeInvalidDrop : function(target, e, id){
18239         return true;
18240     },
18241
18242     // private
18243     handleMouseDown : function(e){
18244         if(this.dragging) {
18245             return;
18246         }
18247         var data = this.getDragData(e);
18248         if(data && this.onBeforeDrag(data, e) !== false){
18249             this.dragData = data;
18250             this.proxy.stop();
18251             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18252         } 
18253     },
18254
18255     /**
18256      * An empty function by default, but provided so that you can perform a custom action before the initial
18257      * drag event begins and optionally cancel it.
18258      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18259      * @param {Event} e The event object
18260      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18261      */
18262     onBeforeDrag : function(data, e){
18263         return true;
18264     },
18265
18266     /**
18267      * An empty function by default, but provided so that you can perform a custom action once the initial
18268      * drag event has begun.  The drag cannot be canceled from this function.
18269      * @param {Number} x The x position of the click on the dragged object
18270      * @param {Number} y The y position of the click on the dragged object
18271      */
18272     onStartDrag : Roo.emptyFn,
18273
18274     // private - YUI override
18275     startDrag : function(x, y){
18276         this.proxy.reset();
18277         this.dragging = true;
18278         this.proxy.update("");
18279         this.onInitDrag(x, y);
18280         this.proxy.show();
18281     },
18282
18283     // private
18284     onInitDrag : function(x, y){
18285         var clone = this.el.dom.cloneNode(true);
18286         clone.id = Roo.id(); // prevent duplicate ids
18287         this.proxy.update(clone);
18288         this.onStartDrag(x, y);
18289         return true;
18290     },
18291
18292     /**
18293      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18294      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18295      */
18296     getProxy : function(){
18297         return this.proxy;  
18298     },
18299
18300     /**
18301      * Hides the drag source's {@link Roo.dd.StatusProxy}
18302      */
18303     hideProxy : function(){
18304         this.proxy.hide();  
18305         this.proxy.reset(true);
18306         this.dragging = false;
18307     },
18308
18309     // private
18310     triggerCacheRefresh : function(){
18311         Roo.dd.DDM.refreshCache(this.groups);
18312     },
18313
18314     // private - override to prevent hiding
18315     b4EndDrag: function(e) {
18316     },
18317
18318     // private - override to prevent moving
18319     endDrag : function(e){
18320         this.onEndDrag(this.dragData, e);
18321     },
18322
18323     // private
18324     onEndDrag : function(data, e){
18325     },
18326     
18327     // private - pin to cursor
18328     autoOffset : function(x, y) {
18329         this.setDelta(-12, -20);
18330     }    
18331 });/*
18332  * Based on:
18333  * Ext JS Library 1.1.1
18334  * Copyright(c) 2006-2007, Ext JS, LLC.
18335  *
18336  * Originally Released Under LGPL - original licence link has changed is not relivant.
18337  *
18338  * Fork - LGPL
18339  * <script type="text/javascript">
18340  */
18341
18342
18343 /**
18344  * @class Roo.dd.DropTarget
18345  * @extends Roo.dd.DDTarget
18346  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18347  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18348  * @constructor
18349  * @param {String/HTMLElement/Element} el The container element
18350  * @param {Object} config
18351  */
18352 Roo.dd.DropTarget = function(el, config){
18353     this.el = Roo.get(el);
18354     
18355     var listeners = false; ;
18356     if (config && config.listeners) {
18357         listeners= config.listeners;
18358         delete config.listeners;
18359     }
18360     Roo.apply(this, config);
18361     
18362     if(this.containerScroll){
18363         Roo.dd.ScrollManager.register(this.el);
18364     }
18365     this.addEvents( {
18366          /**
18367          * @scope Roo.dd.DropTarget
18368          */
18369          
18370          /**
18371          * @event enter
18372          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18373          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18374          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18375          * 
18376          * IMPORTANT : it should set this.overClass and this.dropAllowed
18377          * 
18378          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18379          * @param {Event} e The event
18380          * @param {Object} data An object containing arbitrary data supplied by the drag source
18381          */
18382         "enter" : true,
18383         
18384          /**
18385          * @event over
18386          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18387          * This method will be called on every mouse movement while the drag source is over the drop target.
18388          * This default implementation simply returns the dropAllowed config value.
18389          * 
18390          * IMPORTANT : it should set this.dropAllowed
18391          * 
18392          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18393          * @param {Event} e The event
18394          * @param {Object} data An object containing arbitrary data supplied by the drag source
18395          
18396          */
18397         "over" : true,
18398         /**
18399          * @event out
18400          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18401          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18402          * overClass (if any) from the drop element.
18403          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18404          * @param {Event} e The event
18405          * @param {Object} data An object containing arbitrary data supplied by the drag source
18406          */
18407          "out" : true,
18408          
18409         /**
18410          * @event drop
18411          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18412          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18413          * implementation that does something to process the drop event and returns true so that the drag source's
18414          * repair action does not run.
18415          * 
18416          * IMPORTANT : it should set this.success
18417          * 
18418          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18419          * @param {Event} e The event
18420          * @param {Object} data An object containing arbitrary data supplied by the drag source
18421         */
18422          "drop" : true
18423     });
18424             
18425      
18426     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18427         this.el.dom, 
18428         this.ddGroup || this.group,
18429         {
18430             isTarget: true,
18431             listeners : listeners || {} 
18432            
18433         
18434         }
18435     );
18436
18437 };
18438
18439 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18440     /**
18441      * @cfg {String} overClass
18442      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18443      */
18444      /**
18445      * @cfg {String} ddGroup
18446      * The drag drop group to handle drop events for
18447      */
18448      
18449     /**
18450      * @cfg {String} dropAllowed
18451      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18452      */
18453     dropAllowed : "x-dd-drop-ok",
18454     /**
18455      * @cfg {String} dropNotAllowed
18456      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18457      */
18458     dropNotAllowed : "x-dd-drop-nodrop",
18459     /**
18460      * @cfg {boolean} success
18461      * set this after drop listener.. 
18462      */
18463     success : false,
18464     /**
18465      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18466      * if the drop point is valid for over/enter..
18467      */
18468     valid : false,
18469     // private
18470     isTarget : true,
18471
18472     // private
18473     isNotifyTarget : true,
18474     
18475     /**
18476      * @hide
18477      */
18478     notifyEnter : function(dd, e, data)
18479     {
18480         this.valid = true;
18481         this.fireEvent('enter', dd, e, data);
18482         if(this.overClass){
18483             this.el.addClass(this.overClass);
18484         }
18485         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18486             this.valid ? this.dropAllowed : this.dropNotAllowed
18487         );
18488     },
18489
18490     /**
18491      * @hide
18492      */
18493     notifyOver : function(dd, e, data)
18494     {
18495         this.valid = true;
18496         this.fireEvent('over', dd, e, data);
18497         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18498             this.valid ? this.dropAllowed : this.dropNotAllowed
18499         );
18500     },
18501
18502     /**
18503      * @hide
18504      */
18505     notifyOut : function(dd, e, data)
18506     {
18507         this.fireEvent('out', dd, e, data);
18508         if(this.overClass){
18509             this.el.removeClass(this.overClass);
18510         }
18511     },
18512
18513     /**
18514      * @hide
18515      */
18516     notifyDrop : function(dd, e, data)
18517     {
18518         this.success = false;
18519         this.fireEvent('drop', dd, e, data);
18520         return this.success;
18521     }
18522 });/*
18523  * Based on:
18524  * Ext JS Library 1.1.1
18525  * Copyright(c) 2006-2007, Ext JS, LLC.
18526  *
18527  * Originally Released Under LGPL - original licence link has changed is not relivant.
18528  *
18529  * Fork - LGPL
18530  * <script type="text/javascript">
18531  */
18532
18533
18534 /**
18535  * @class Roo.dd.DragZone
18536  * @extends Roo.dd.DragSource
18537  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18538  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18539  * @constructor
18540  * @param {String/HTMLElement/Element} el The container element
18541  * @param {Object} config
18542  */
18543 Roo.dd.DragZone = function(el, config){
18544     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18545     if(this.containerScroll){
18546         Roo.dd.ScrollManager.register(this.el);
18547     }
18548 };
18549
18550 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18551     /**
18552      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18553      * for auto scrolling during drag operations.
18554      */
18555     /**
18556      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18557      * method after a failed drop (defaults to "c3daf9" - light blue)
18558      */
18559
18560     /**
18561      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18562      * for a valid target to drag based on the mouse down. Override this method
18563      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18564      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18565      * @param {EventObject} e The mouse down event
18566      * @return {Object} The dragData
18567      */
18568     getDragData : function(e){
18569         return Roo.dd.Registry.getHandleFromEvent(e);
18570     },
18571     
18572     /**
18573      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18574      * this.dragData.ddel
18575      * @param {Number} x The x position of the click on the dragged object
18576      * @param {Number} y The y position of the click on the dragged object
18577      * @return {Boolean} true to continue the drag, false to cancel
18578      */
18579     onInitDrag : function(x, y){
18580         this.proxy.update(this.dragData.ddel.cloneNode(true));
18581         this.onStartDrag(x, y);
18582         return true;
18583     },
18584     
18585     /**
18586      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18587      */
18588     afterRepair : function(){
18589         if(Roo.enableFx){
18590             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18591         }
18592         this.dragging = false;
18593     },
18594
18595     /**
18596      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18597      * the XY of this.dragData.ddel
18598      * @param {EventObject} e The mouse up event
18599      * @return {Array} The xy location (e.g. [100, 200])
18600      */
18601     getRepairXY : function(e){
18602         return Roo.Element.fly(this.dragData.ddel).getXY();  
18603     }
18604 });/*
18605  * Based on:
18606  * Ext JS Library 1.1.1
18607  * Copyright(c) 2006-2007, Ext JS, LLC.
18608  *
18609  * Originally Released Under LGPL - original licence link has changed is not relivant.
18610  *
18611  * Fork - LGPL
18612  * <script type="text/javascript">
18613  */
18614 /**
18615  * @class Roo.dd.DropZone
18616  * @extends Roo.dd.DropTarget
18617  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18618  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18619  * @constructor
18620  * @param {String/HTMLElement/Element} el The container element
18621  * @param {Object} config
18622  */
18623 Roo.dd.DropZone = function(el, config){
18624     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18625 };
18626
18627 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18628     /**
18629      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18630      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18631      * provide your own custom lookup.
18632      * @param {Event} e The event
18633      * @return {Object} data The custom data
18634      */
18635     getTargetFromEvent : function(e){
18636         return Roo.dd.Registry.getTargetFromEvent(e);
18637     },
18638
18639     /**
18640      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18641      * that it has registered.  This method has no default implementation and should be overridden to provide
18642      * node-specific processing if necessary.
18643      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18644      * {@link #getTargetFromEvent} for this node)
18645      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18646      * @param {Event} e The event
18647      * @param {Object} data An object containing arbitrary data supplied by the drag source
18648      */
18649     onNodeEnter : function(n, dd, e, data){
18650         
18651     },
18652
18653     /**
18654      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18655      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18656      * overridden to provide the proper feedback.
18657      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18658      * {@link #getTargetFromEvent} for this node)
18659      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18660      * @param {Event} e The event
18661      * @param {Object} data An object containing arbitrary data supplied by the drag source
18662      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18663      * underlying {@link Roo.dd.StatusProxy} can be updated
18664      */
18665     onNodeOver : function(n, dd, e, data){
18666         return this.dropAllowed;
18667     },
18668
18669     /**
18670      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18671      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18672      * node-specific processing if necessary.
18673      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18674      * {@link #getTargetFromEvent} for this node)
18675      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18676      * @param {Event} e The event
18677      * @param {Object} data An object containing arbitrary data supplied by the drag source
18678      */
18679     onNodeOut : function(n, dd, e, data){
18680         
18681     },
18682
18683     /**
18684      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18685      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18686      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18687      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18688      * {@link #getTargetFromEvent} for this node)
18689      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18690      * @param {Event} e The event
18691      * @param {Object} data An object containing arbitrary data supplied by the drag source
18692      * @return {Boolean} True if the drop was valid, else false
18693      */
18694     onNodeDrop : function(n, dd, e, data){
18695         return false;
18696     },
18697
18698     /**
18699      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18700      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18701      * it should be overridden to provide the proper feedback if necessary.
18702      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18703      * @param {Event} e The event
18704      * @param {Object} data An object containing arbitrary data supplied by the drag source
18705      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18706      * underlying {@link Roo.dd.StatusProxy} can be updated
18707      */
18708     onContainerOver : function(dd, e, data){
18709         return this.dropNotAllowed;
18710     },
18711
18712     /**
18713      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18714      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18715      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18716      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18717      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18718      * @param {Event} e The event
18719      * @param {Object} data An object containing arbitrary data supplied by the drag source
18720      * @return {Boolean} True if the drop was valid, else false
18721      */
18722     onContainerDrop : function(dd, e, data){
18723         return false;
18724     },
18725
18726     /**
18727      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18728      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18729      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18730      * you should override this method and provide a custom implementation.
18731      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18732      * @param {Event} e The event
18733      * @param {Object} data An object containing arbitrary data supplied by the drag source
18734      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18735      * underlying {@link Roo.dd.StatusProxy} can be updated
18736      */
18737     notifyEnter : function(dd, e, data){
18738         return this.dropNotAllowed;
18739     },
18740
18741     /**
18742      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18743      * This method will be called on every mouse movement while the drag source is over the drop zone.
18744      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18745      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18746      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18747      * registered node, it will call {@link #onContainerOver}.
18748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18749      * @param {Event} e The event
18750      * @param {Object} data An object containing arbitrary data supplied by the drag source
18751      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18752      * underlying {@link Roo.dd.StatusProxy} can be updated
18753      */
18754     notifyOver : function(dd, e, data){
18755         var n = this.getTargetFromEvent(e);
18756         if(!n){ // not over valid drop target
18757             if(this.lastOverNode){
18758                 this.onNodeOut(this.lastOverNode, dd, e, data);
18759                 this.lastOverNode = null;
18760             }
18761             return this.onContainerOver(dd, e, data);
18762         }
18763         if(this.lastOverNode != n){
18764             if(this.lastOverNode){
18765                 this.onNodeOut(this.lastOverNode, dd, e, data);
18766             }
18767             this.onNodeEnter(n, dd, e, data);
18768             this.lastOverNode = n;
18769         }
18770         return this.onNodeOver(n, dd, e, data);
18771     },
18772
18773     /**
18774      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18775      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18776      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18777      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18778      * @param {Event} e The event
18779      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18780      */
18781     notifyOut : function(dd, e, data){
18782         if(this.lastOverNode){
18783             this.onNodeOut(this.lastOverNode, dd, e, data);
18784             this.lastOverNode = null;
18785         }
18786     },
18787
18788     /**
18789      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18790      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18791      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18792      * otherwise it will call {@link #onContainerDrop}.
18793      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18794      * @param {Event} e The event
18795      * @param {Object} data An object containing arbitrary data supplied by the drag source
18796      * @return {Boolean} True if the drop was valid, else false
18797      */
18798     notifyDrop : function(dd, e, data){
18799         if(this.lastOverNode){
18800             this.onNodeOut(this.lastOverNode, dd, e, data);
18801             this.lastOverNode = null;
18802         }
18803         var n = this.getTargetFromEvent(e);
18804         return n ?
18805             this.onNodeDrop(n, dd, e, data) :
18806             this.onContainerDrop(dd, e, data);
18807     },
18808
18809     // private
18810     triggerCacheRefresh : function(){
18811         Roo.dd.DDM.refreshCache(this.groups);
18812     }  
18813 });/*
18814  * Based on:
18815  * Ext JS Library 1.1.1
18816  * Copyright(c) 2006-2007, Ext JS, LLC.
18817  *
18818  * Originally Released Under LGPL - original licence link has changed is not relivant.
18819  *
18820  * Fork - LGPL
18821  * <script type="text/javascript">
18822  */
18823
18824
18825 /**
18826  * @class Roo.data.SortTypes
18827  * @singleton
18828  * Defines the default sorting (casting?) comparison functions used when sorting data.
18829  */
18830 Roo.data.SortTypes = {
18831     /**
18832      * Default sort that does nothing
18833      * @param {Mixed} s The value being converted
18834      * @return {Mixed} The comparison value
18835      */
18836     none : function(s){
18837         return s;
18838     },
18839     
18840     /**
18841      * The regular expression used to strip tags
18842      * @type {RegExp}
18843      * @property
18844      */
18845     stripTagsRE : /<\/?[^>]+>/gi,
18846     
18847     /**
18848      * Strips all HTML tags to sort on text only
18849      * @param {Mixed} s The value being converted
18850      * @return {String} The comparison value
18851      */
18852     asText : function(s){
18853         return String(s).replace(this.stripTagsRE, "");
18854     },
18855     
18856     /**
18857      * Strips all HTML tags to sort on text only - Case insensitive
18858      * @param {Mixed} s The value being converted
18859      * @return {String} The comparison value
18860      */
18861     asUCText : function(s){
18862         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18863     },
18864     
18865     /**
18866      * Case insensitive string
18867      * @param {Mixed} s The value being converted
18868      * @return {String} The comparison value
18869      */
18870     asUCString : function(s) {
18871         return String(s).toUpperCase();
18872     },
18873     
18874     /**
18875      * Date sorting
18876      * @param {Mixed} s The value being converted
18877      * @return {Number} The comparison value
18878      */
18879     asDate : function(s) {
18880         if(!s){
18881             return 0;
18882         }
18883         if(s instanceof Date){
18884             return s.getTime();
18885         }
18886         return Date.parse(String(s));
18887     },
18888     
18889     /**
18890      * Float sorting
18891      * @param {Mixed} s The value being converted
18892      * @return {Float} The comparison value
18893      */
18894     asFloat : function(s) {
18895         var val = parseFloat(String(s).replace(/,/g, ""));
18896         if(isNaN(val)) val = 0;
18897         return val;
18898     },
18899     
18900     /**
18901      * Integer sorting
18902      * @param {Mixed} s The value being converted
18903      * @return {Number} The comparison value
18904      */
18905     asInt : function(s) {
18906         var val = parseInt(String(s).replace(/,/g, ""));
18907         if(isNaN(val)) val = 0;
18908         return val;
18909     }
18910 };/*
18911  * Based on:
18912  * Ext JS Library 1.1.1
18913  * Copyright(c) 2006-2007, Ext JS, LLC.
18914  *
18915  * Originally Released Under LGPL - original licence link has changed is not relivant.
18916  *
18917  * Fork - LGPL
18918  * <script type="text/javascript">
18919  */
18920
18921 /**
18922 * @class Roo.data.Record
18923  * Instances of this class encapsulate both record <em>definition</em> information, and record
18924  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18925  * to access Records cached in an {@link Roo.data.Store} object.<br>
18926  * <p>
18927  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18928  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18929  * objects.<br>
18930  * <p>
18931  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18932  * @constructor
18933  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18934  * {@link #create}. The parameters are the same.
18935  * @param {Array} data An associative Array of data values keyed by the field name.
18936  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18937  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18938  * not specified an integer id is generated.
18939  */
18940 Roo.data.Record = function(data, id){
18941     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18942     this.data = data;
18943 };
18944
18945 /**
18946  * Generate a constructor for a specific record layout.
18947  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18948  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18949  * Each field definition object may contain the following properties: <ul>
18950  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18951  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18952  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18953  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18954  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18955  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18956  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18957  * this may be omitted.</p></li>
18958  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18959  * <ul><li>auto (Default, implies no conversion)</li>
18960  * <li>string</li>
18961  * <li>int</li>
18962  * <li>float</li>
18963  * <li>boolean</li>
18964  * <li>date</li></ul></p></li>
18965  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18966  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18967  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18968  * by the Reader into an object that will be stored in the Record. It is passed the
18969  * following parameters:<ul>
18970  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18971  * </ul></p></li>
18972  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18973  * </ul>
18974  * <br>usage:<br><pre><code>
18975 var TopicRecord = Roo.data.Record.create(
18976     {name: 'title', mapping: 'topic_title'},
18977     {name: 'author', mapping: 'username'},
18978     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18979     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18980     {name: 'lastPoster', mapping: 'user2'},
18981     {name: 'excerpt', mapping: 'post_text'}
18982 );
18983
18984 var myNewRecord = new TopicRecord({
18985     title: 'Do my job please',
18986     author: 'noobie',
18987     totalPosts: 1,
18988     lastPost: new Date(),
18989     lastPoster: 'Animal',
18990     excerpt: 'No way dude!'
18991 });
18992 myStore.add(myNewRecord);
18993 </code></pre>
18994  * @method create
18995  * @static
18996  */
18997 Roo.data.Record.create = function(o){
18998     var f = function(){
18999         f.superclass.constructor.apply(this, arguments);
19000     };
19001     Roo.extend(f, Roo.data.Record);
19002     var p = f.prototype;
19003     p.fields = new Roo.util.MixedCollection(false, function(field){
19004         return field.name;
19005     });
19006     for(var i = 0, len = o.length; i < len; i++){
19007         p.fields.add(new Roo.data.Field(o[i]));
19008     }
19009     f.getField = function(name){
19010         return p.fields.get(name);  
19011     };
19012     return f;
19013 };
19014
19015 Roo.data.Record.AUTO_ID = 1000;
19016 Roo.data.Record.EDIT = 'edit';
19017 Roo.data.Record.REJECT = 'reject';
19018 Roo.data.Record.COMMIT = 'commit';
19019
19020 Roo.data.Record.prototype = {
19021     /**
19022      * Readonly flag - true if this record has been modified.
19023      * @type Boolean
19024      */
19025     dirty : false,
19026     editing : false,
19027     error: null,
19028     modified: null,
19029
19030     // private
19031     join : function(store){
19032         this.store = store;
19033     },
19034
19035     /**
19036      * Set the named field to the specified value.
19037      * @param {String} name The name of the field to set.
19038      * @param {Object} value The value to set the field to.
19039      */
19040     set : function(name, value){
19041         if(this.data[name] == value){
19042             return;
19043         }
19044         this.dirty = true;
19045         if(!this.modified){
19046             this.modified = {};
19047         }
19048         if(typeof this.modified[name] == 'undefined'){
19049             this.modified[name] = this.data[name];
19050         }
19051         this.data[name] = value;
19052         if(!this.editing){
19053             this.store.afterEdit(this);
19054         }       
19055     },
19056
19057     /**
19058      * Get the value of the named field.
19059      * @param {String} name The name of the field to get the value of.
19060      * @return {Object} The value of the field.
19061      */
19062     get : function(name){
19063         return this.data[name]; 
19064     },
19065
19066     // private
19067     beginEdit : function(){
19068         this.editing = true;
19069         this.modified = {}; 
19070     },
19071
19072     // private
19073     cancelEdit : function(){
19074         this.editing = false;
19075         delete this.modified;
19076     },
19077
19078     // private
19079     endEdit : function(){
19080         this.editing = false;
19081         if(this.dirty && this.store){
19082             this.store.afterEdit(this);
19083         }
19084     },
19085
19086     /**
19087      * Usually called by the {@link Roo.data.Store} which owns the Record.
19088      * Rejects all changes made to the Record since either creation, or the last commit operation.
19089      * Modified fields are reverted to their original values.
19090      * <p>
19091      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19092      * of reject operations.
19093      */
19094     reject : function(){
19095         var m = this.modified;
19096         for(var n in m){
19097             if(typeof m[n] != "function"){
19098                 this.data[n] = m[n];
19099             }
19100         }
19101         this.dirty = false;
19102         delete this.modified;
19103         this.editing = false;
19104         if(this.store){
19105             this.store.afterReject(this);
19106         }
19107     },
19108
19109     /**
19110      * Usually called by the {@link Roo.data.Store} which owns the Record.
19111      * Commits all changes made to the Record since either creation, or the last commit operation.
19112      * <p>
19113      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19114      * of commit operations.
19115      */
19116     commit : function(){
19117         this.dirty = false;
19118         delete this.modified;
19119         this.editing = false;
19120         if(this.store){
19121             this.store.afterCommit(this);
19122         }
19123     },
19124
19125     // private
19126     hasError : function(){
19127         return this.error != null;
19128     },
19129
19130     // private
19131     clearError : function(){
19132         this.error = null;
19133     },
19134
19135     /**
19136      * Creates a copy of this record.
19137      * @param {String} id (optional) A new record id if you don't want to use this record's id
19138      * @return {Record}
19139      */
19140     copy : function(newId) {
19141         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19142     }
19143 };/*
19144  * Based on:
19145  * Ext JS Library 1.1.1
19146  * Copyright(c) 2006-2007, Ext JS, LLC.
19147  *
19148  * Originally Released Under LGPL - original licence link has changed is not relivant.
19149  *
19150  * Fork - LGPL
19151  * <script type="text/javascript">
19152  */
19153
19154
19155
19156 /**
19157  * @class Roo.data.Store
19158  * @extends Roo.util.Observable
19159  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19160  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19161  * <p>
19162  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19163  * has no knowledge of the format of the data returned by the Proxy.<br>
19164  * <p>
19165  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19166  * instances from the data object. These records are cached and made available through accessor functions.
19167  * @constructor
19168  * Creates a new Store.
19169  * @param {Object} config A config object containing the objects needed for the Store to access data,
19170  * and read the data into Records.
19171  */
19172 Roo.data.Store = function(config){
19173     this.data = new Roo.util.MixedCollection(false);
19174     this.data.getKey = function(o){
19175         return o.id;
19176     };
19177     this.baseParams = {};
19178     // private
19179     this.paramNames = {
19180         "start" : "start",
19181         "limit" : "limit",
19182         "sort" : "sort",
19183         "dir" : "dir",
19184         "multisort" : "_multisort"
19185     };
19186
19187     if(config && config.data){
19188         this.inlineData = config.data;
19189         delete config.data;
19190     }
19191
19192     Roo.apply(this, config);
19193     
19194     if(this.reader){ // reader passed
19195         this.reader = Roo.factory(this.reader, Roo.data);
19196         this.reader.xmodule = this.xmodule || false;
19197         if(!this.recordType){
19198             this.recordType = this.reader.recordType;
19199         }
19200         if(this.reader.onMetaChange){
19201             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19202         }
19203     }
19204
19205     if(this.recordType){
19206         this.fields = this.recordType.prototype.fields;
19207     }
19208     this.modified = [];
19209
19210     this.addEvents({
19211         /**
19212          * @event datachanged
19213          * Fires when the data cache has changed, and a widget which is using this Store
19214          * as a Record cache should refresh its view.
19215          * @param {Store} this
19216          */
19217         datachanged : true,
19218         /**
19219          * @event metachange
19220          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19221          * @param {Store} this
19222          * @param {Object} meta The JSON metadata
19223          */
19224         metachange : true,
19225         /**
19226          * @event add
19227          * Fires when Records have been added to the Store
19228          * @param {Store} this
19229          * @param {Roo.data.Record[]} records The array of Records added
19230          * @param {Number} index The index at which the record(s) were added
19231          */
19232         add : true,
19233         /**
19234          * @event remove
19235          * Fires when a Record has been removed from the Store
19236          * @param {Store} this
19237          * @param {Roo.data.Record} record The Record that was removed
19238          * @param {Number} index The index at which the record was removed
19239          */
19240         remove : true,
19241         /**
19242          * @event update
19243          * Fires when a Record has been updated
19244          * @param {Store} this
19245          * @param {Roo.data.Record} record The Record that was updated
19246          * @param {String} operation The update operation being performed.  Value may be one of:
19247          * <pre><code>
19248  Roo.data.Record.EDIT
19249  Roo.data.Record.REJECT
19250  Roo.data.Record.COMMIT
19251          * </code></pre>
19252          */
19253         update : true,
19254         /**
19255          * @event clear
19256          * Fires when the data cache has been cleared.
19257          * @param {Store} this
19258          */
19259         clear : true,
19260         /**
19261          * @event beforeload
19262          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19263          * the load action will be canceled.
19264          * @param {Store} this
19265          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19266          */
19267         beforeload : true,
19268         /**
19269          * @event load
19270          * Fires after a new set of Records has been loaded.
19271          * @param {Store} this
19272          * @param {Roo.data.Record[]} records The Records that were loaded
19273          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19274          */
19275         load : true,
19276         /**
19277          * @event loadexception
19278          * Fires if an exception occurs in the Proxy during loading.
19279          * Called with the signature of the Proxy's "loadexception" event.
19280          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19281          * 
19282          * @param {Proxy} 
19283          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19284          * @param {Object} load options 
19285          * @param {Object} jsonData from your request (normally this contains the Exception)
19286          */
19287         loadexception : true
19288     });
19289     
19290     if(this.proxy){
19291         this.proxy = Roo.factory(this.proxy, Roo.data);
19292         this.proxy.xmodule = this.xmodule || false;
19293         this.relayEvents(this.proxy,  ["loadexception"]);
19294     }
19295     this.sortToggle = {};
19296     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19297
19298     Roo.data.Store.superclass.constructor.call(this);
19299
19300     if(this.inlineData){
19301         this.loadData(this.inlineData);
19302         delete this.inlineData;
19303     }
19304 };
19305 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19306      /**
19307     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19308     * without a remote query - used by combo/forms at present.
19309     */
19310     
19311     /**
19312     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19313     */
19314     /**
19315     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19316     */
19317     /**
19318     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19319     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19320     */
19321     /**
19322     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19323     * on any HTTP request
19324     */
19325     /**
19326     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19327     */
19328     /**
19329     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19330     */
19331     multiSort: false,
19332     /**
19333     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19334     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19335     */
19336     remoteSort : false,
19337
19338     /**
19339     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19340      * loaded or when a record is removed. (defaults to false).
19341     */
19342     pruneModifiedRecords : false,
19343
19344     // private
19345     lastOptions : null,
19346
19347     /**
19348      * Add Records to the Store and fires the add event.
19349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19350      */
19351     add : function(records){
19352         records = [].concat(records);
19353         for(var i = 0, len = records.length; i < len; i++){
19354             records[i].join(this);
19355         }
19356         var index = this.data.length;
19357         this.data.addAll(records);
19358         this.fireEvent("add", this, records, index);
19359     },
19360
19361     /**
19362      * Remove a Record from the Store and fires the remove event.
19363      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19364      */
19365     remove : function(record){
19366         var index = this.data.indexOf(record);
19367         this.data.removeAt(index);
19368         if(this.pruneModifiedRecords){
19369             this.modified.remove(record);
19370         }
19371         this.fireEvent("remove", this, record, index);
19372     },
19373
19374     /**
19375      * Remove all Records from the Store and fires the clear event.
19376      */
19377     removeAll : function(){
19378         this.data.clear();
19379         if(this.pruneModifiedRecords){
19380             this.modified = [];
19381         }
19382         this.fireEvent("clear", this);
19383     },
19384
19385     /**
19386      * Inserts Records to the Store at the given index and fires the add event.
19387      * @param {Number} index The start index at which to insert the passed Records.
19388      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19389      */
19390     insert : function(index, records){
19391         records = [].concat(records);
19392         for(var i = 0, len = records.length; i < len; i++){
19393             this.data.insert(index, records[i]);
19394             records[i].join(this);
19395         }
19396         this.fireEvent("add", this, records, index);
19397     },
19398
19399     /**
19400      * Get the index within the cache of the passed Record.
19401      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19402      * @return {Number} The index of the passed Record. Returns -1 if not found.
19403      */
19404     indexOf : function(record){
19405         return this.data.indexOf(record);
19406     },
19407
19408     /**
19409      * Get the index within the cache of the Record with the passed id.
19410      * @param {String} id The id of the Record to find.
19411      * @return {Number} The index of the Record. Returns -1 if not found.
19412      */
19413     indexOfId : function(id){
19414         return this.data.indexOfKey(id);
19415     },
19416
19417     /**
19418      * Get the Record with the specified id.
19419      * @param {String} id The id of the Record to find.
19420      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19421      */
19422     getById : function(id){
19423         return this.data.key(id);
19424     },
19425
19426     /**
19427      * Get the Record at the specified index.
19428      * @param {Number} index The index of the Record to find.
19429      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19430      */
19431     getAt : function(index){
19432         return this.data.itemAt(index);
19433     },
19434
19435     /**
19436      * Returns a range of Records between specified indices.
19437      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19438      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19439      * @return {Roo.data.Record[]} An array of Records
19440      */
19441     getRange : function(start, end){
19442         return this.data.getRange(start, end);
19443     },
19444
19445     // private
19446     storeOptions : function(o){
19447         o = Roo.apply({}, o);
19448         delete o.callback;
19449         delete o.scope;
19450         this.lastOptions = o;
19451     },
19452
19453     /**
19454      * Loads the Record cache from the configured Proxy using the configured Reader.
19455      * <p>
19456      * If using remote paging, then the first load call must specify the <em>start</em>
19457      * and <em>limit</em> properties in the options.params property to establish the initial
19458      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19459      * <p>
19460      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19461      * and this call will return before the new data has been loaded. Perform any post-processing
19462      * in a callback function, or in a "load" event handler.</strong>
19463      * <p>
19464      * @param {Object} options An object containing properties which control loading options:<ul>
19465      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19466      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19467      * passed the following arguments:<ul>
19468      * <li>r : Roo.data.Record[]</li>
19469      * <li>options: Options object from the load call</li>
19470      * <li>success: Boolean success indicator</li></ul></li>
19471      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19472      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19473      * </ul>
19474      */
19475     load : function(options){
19476         options = options || {};
19477         if(this.fireEvent("beforeload", this, options) !== false){
19478             this.storeOptions(options);
19479             var p = Roo.apply(options.params || {}, this.baseParams);
19480             // if meta was not loaded from remote source.. try requesting it.
19481             if (!this.reader.metaFromRemote) {
19482                 p._requestMeta = 1;
19483             }
19484             if(this.sortInfo && this.remoteSort){
19485                 var pn = this.paramNames;
19486                 p[pn["sort"]] = this.sortInfo.field;
19487                 p[pn["dir"]] = this.sortInfo.direction;
19488             }
19489             if (this.multiSort) {
19490                 var pn = this.paramNames;
19491                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19492             }
19493             
19494             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19495         }
19496     },
19497
19498     /**
19499      * Reloads the Record cache from the configured Proxy using the configured Reader and
19500      * the options from the last load operation performed.
19501      * @param {Object} options (optional) An object containing properties which may override the options
19502      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19503      * the most recently used options are reused).
19504      */
19505     reload : function(options){
19506         this.load(Roo.applyIf(options||{}, this.lastOptions));
19507     },
19508
19509     // private
19510     // Called as a callback by the Reader during a load operation.
19511     loadRecords : function(o, options, success){
19512         if(!o || success === false){
19513             if(success !== false){
19514                 this.fireEvent("load", this, [], options);
19515             }
19516             if(options.callback){
19517                 options.callback.call(options.scope || this, [], options, false);
19518             }
19519             return;
19520         }
19521         // if data returned failure - throw an exception.
19522         if (o.success === false) {
19523             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19524             return;
19525         }
19526         var r = o.records, t = o.totalRecords || r.length;
19527         if(!options || options.add !== true){
19528             if(this.pruneModifiedRecords){
19529                 this.modified = [];
19530             }
19531             for(var i = 0, len = r.length; i < len; i++){
19532                 r[i].join(this);
19533             }
19534             if(this.snapshot){
19535                 this.data = this.snapshot;
19536                 delete this.snapshot;
19537             }
19538             this.data.clear();
19539             this.data.addAll(r);
19540             this.totalLength = t;
19541             this.applySort();
19542             this.fireEvent("datachanged", this);
19543         }else{
19544             this.totalLength = Math.max(t, this.data.length+r.length);
19545             this.add(r);
19546         }
19547         this.fireEvent("load", this, r, options);
19548         if(options.callback){
19549             options.callback.call(options.scope || this, r, options, true);
19550         }
19551     },
19552
19553     /**
19554      * Loads data from a passed data block. A Reader which understands the format of the data
19555      * must have been configured in the constructor.
19556      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19557      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19558      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19559      */
19560     loadData : function(o, append){
19561         var r = this.reader.readRecords(o);
19562         this.loadRecords(r, {add: append}, true);
19563     },
19564
19565     /**
19566      * Gets the number of cached records.
19567      * <p>
19568      * <em>If using paging, this may not be the total size of the dataset. If the data object
19569      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19570      * the data set size</em>
19571      */
19572     getCount : function(){
19573         return this.data.length || 0;
19574     },
19575
19576     /**
19577      * Gets the total number of records in the dataset as returned by the server.
19578      * <p>
19579      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19580      * the dataset size</em>
19581      */
19582     getTotalCount : function(){
19583         return this.totalLength || 0;
19584     },
19585
19586     /**
19587      * Returns the sort state of the Store as an object with two properties:
19588      * <pre><code>
19589  field {String} The name of the field by which the Records are sorted
19590  direction {String} The sort order, "ASC" or "DESC"
19591      * </code></pre>
19592      */
19593     getSortState : function(){
19594         return this.sortInfo;
19595     },
19596
19597     // private
19598     applySort : function(){
19599         if(this.sortInfo && !this.remoteSort){
19600             var s = this.sortInfo, f = s.field;
19601             var st = this.fields.get(f).sortType;
19602             var fn = function(r1, r2){
19603                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19604                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19605             };
19606             this.data.sort(s.direction, fn);
19607             if(this.snapshot && this.snapshot != this.data){
19608                 this.snapshot.sort(s.direction, fn);
19609             }
19610         }
19611     },
19612
19613     /**
19614      * Sets the default sort column and order to be used by the next load operation.
19615      * @param {String} fieldName The name of the field to sort by.
19616      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19617      */
19618     setDefaultSort : function(field, dir){
19619         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19620     },
19621
19622     /**
19623      * Sort the Records.
19624      * If remote sorting is used, the sort is performed on the server, and the cache is
19625      * reloaded. If local sorting is used, the cache is sorted internally.
19626      * @param {String} fieldName The name of the field to sort by.
19627      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19628      */
19629     sort : function(fieldName, dir){
19630         var f = this.fields.get(fieldName);
19631         if(!dir){
19632             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19633             
19634             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19635                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19636             }else{
19637                 dir = f.sortDir;
19638             }
19639         }
19640         this.sortToggle[f.name] = dir;
19641         this.sortInfo = {field: f.name, direction: dir};
19642         if(!this.remoteSort){
19643             this.applySort();
19644             this.fireEvent("datachanged", this);
19645         }else{
19646             this.load(this.lastOptions);
19647         }
19648     },
19649
19650     /**
19651      * Calls the specified function for each of the Records in the cache.
19652      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19653      * Returning <em>false</em> aborts and exits the iteration.
19654      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19655      */
19656     each : function(fn, scope){
19657         this.data.each(fn, scope);
19658     },
19659
19660     /**
19661      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19662      * (e.g., during paging).
19663      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19664      */
19665     getModifiedRecords : function(){
19666         return this.modified;
19667     },
19668
19669     // private
19670     createFilterFn : function(property, value, anyMatch){
19671         if(!value.exec){ // not a regex
19672             value = String(value);
19673             if(value.length == 0){
19674                 return false;
19675             }
19676             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19677         }
19678         return function(r){
19679             return value.test(r.data[property]);
19680         };
19681     },
19682
19683     /**
19684      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19685      * @param {String} property A field on your records
19686      * @param {Number} start The record index to start at (defaults to 0)
19687      * @param {Number} end The last record index to include (defaults to length - 1)
19688      * @return {Number} The sum
19689      */
19690     sum : function(property, start, end){
19691         var rs = this.data.items, v = 0;
19692         start = start || 0;
19693         end = (end || end === 0) ? end : rs.length-1;
19694
19695         for(var i = start; i <= end; i++){
19696             v += (rs[i].data[property] || 0);
19697         }
19698         return v;
19699     },
19700
19701     /**
19702      * Filter the records by a specified property.
19703      * @param {String} field A field on your records
19704      * @param {String/RegExp} value Either a string that the field
19705      * should start with or a RegExp to test against the field
19706      * @param {Boolean} anyMatch True to match any part not just the beginning
19707      */
19708     filter : function(property, value, anyMatch){
19709         var fn = this.createFilterFn(property, value, anyMatch);
19710         return fn ? this.filterBy(fn) : this.clearFilter();
19711     },
19712
19713     /**
19714      * Filter by a function. The specified function will be called with each
19715      * record in this data source. If the function returns true the record is included,
19716      * otherwise it is filtered.
19717      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19718      * @param {Object} scope (optional) The scope of the function (defaults to this)
19719      */
19720     filterBy : function(fn, scope){
19721         this.snapshot = this.snapshot || this.data;
19722         this.data = this.queryBy(fn, scope||this);
19723         this.fireEvent("datachanged", this);
19724     },
19725
19726     /**
19727      * Query the records by a specified property.
19728      * @param {String} field A field on your records
19729      * @param {String/RegExp} value Either a string that the field
19730      * should start with or a RegExp to test against the field
19731      * @param {Boolean} anyMatch True to match any part not just the beginning
19732      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19733      */
19734     query : function(property, value, anyMatch){
19735         var fn = this.createFilterFn(property, value, anyMatch);
19736         return fn ? this.queryBy(fn) : this.data.clone();
19737     },
19738
19739     /**
19740      * Query by a function. The specified function will be called with each
19741      * record in this data source. If the function returns true the record is included
19742      * in the results.
19743      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19744      * @param {Object} scope (optional) The scope of the function (defaults to this)
19745       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19746      **/
19747     queryBy : function(fn, scope){
19748         var data = this.snapshot || this.data;
19749         return data.filterBy(fn, scope||this);
19750     },
19751
19752     /**
19753      * Collects unique values for a particular dataIndex from this store.
19754      * @param {String} dataIndex The property to collect
19755      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19756      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19757      * @return {Array} An array of the unique values
19758      **/
19759     collect : function(dataIndex, allowNull, bypassFilter){
19760         var d = (bypassFilter === true && this.snapshot) ?
19761                 this.snapshot.items : this.data.items;
19762         var v, sv, r = [], l = {};
19763         for(var i = 0, len = d.length; i < len; i++){
19764             v = d[i].data[dataIndex];
19765             sv = String(v);
19766             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19767                 l[sv] = true;
19768                 r[r.length] = v;
19769             }
19770         }
19771         return r;
19772     },
19773
19774     /**
19775      * Revert to a view of the Record cache with no filtering applied.
19776      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19777      */
19778     clearFilter : function(suppressEvent){
19779         if(this.snapshot && this.snapshot != this.data){
19780             this.data = this.snapshot;
19781             delete this.snapshot;
19782             if(suppressEvent !== true){
19783                 this.fireEvent("datachanged", this);
19784             }
19785         }
19786     },
19787
19788     // private
19789     afterEdit : function(record){
19790         if(this.modified.indexOf(record) == -1){
19791             this.modified.push(record);
19792         }
19793         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19794     },
19795
19796     // private
19797     afterReject : function(record){
19798         this.modified.remove(record);
19799         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19800     },
19801
19802     // private
19803     afterCommit : function(record){
19804         this.modified.remove(record);
19805         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19806     },
19807
19808     /**
19809      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19810      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19811      */
19812     commitChanges : function(){
19813         var m = this.modified.slice(0);
19814         this.modified = [];
19815         for(var i = 0, len = m.length; i < len; i++){
19816             m[i].commit();
19817         }
19818     },
19819
19820     /**
19821      * Cancel outstanding changes on all changed records.
19822      */
19823     rejectChanges : function(){
19824         var m = this.modified.slice(0);
19825         this.modified = [];
19826         for(var i = 0, len = m.length; i < len; i++){
19827             m[i].reject();
19828         }
19829     },
19830
19831     onMetaChange : function(meta, rtype, o){
19832         this.recordType = rtype;
19833         this.fields = rtype.prototype.fields;
19834         delete this.snapshot;
19835         this.sortInfo = meta.sortInfo || this.sortInfo;
19836         this.modified = [];
19837         this.fireEvent('metachange', this, this.reader.meta);
19838     }
19839 });/*
19840  * Based on:
19841  * Ext JS Library 1.1.1
19842  * Copyright(c) 2006-2007, Ext JS, LLC.
19843  *
19844  * Originally Released Under LGPL - original licence link has changed is not relivant.
19845  *
19846  * Fork - LGPL
19847  * <script type="text/javascript">
19848  */
19849
19850 /**
19851  * @class Roo.data.SimpleStore
19852  * @extends Roo.data.Store
19853  * Small helper class to make creating Stores from Array data easier.
19854  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19855  * @cfg {Array} fields An array of field definition objects, or field name strings.
19856  * @cfg {Array} data The multi-dimensional array of data
19857  * @constructor
19858  * @param {Object} config
19859  */
19860 Roo.data.SimpleStore = function(config){
19861     Roo.data.SimpleStore.superclass.constructor.call(this, {
19862         isLocal : true,
19863         reader: new Roo.data.ArrayReader({
19864                 id: config.id
19865             },
19866             Roo.data.Record.create(config.fields)
19867         ),
19868         proxy : new Roo.data.MemoryProxy(config.data)
19869     });
19870     this.load();
19871 };
19872 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19873  * Based on:
19874  * Ext JS Library 1.1.1
19875  * Copyright(c) 2006-2007, Ext JS, LLC.
19876  *
19877  * Originally Released Under LGPL - original licence link has changed is not relivant.
19878  *
19879  * Fork - LGPL
19880  * <script type="text/javascript">
19881  */
19882
19883 /**
19884 /**
19885  * @extends Roo.data.Store
19886  * @class Roo.data.JsonStore
19887  * Small helper class to make creating Stores for JSON data easier. <br/>
19888 <pre><code>
19889 var store = new Roo.data.JsonStore({
19890     url: 'get-images.php',
19891     root: 'images',
19892     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19893 });
19894 </code></pre>
19895  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19896  * JsonReader and HttpProxy (unless inline data is provided).</b>
19897  * @cfg {Array} fields An array of field definition objects, or field name strings.
19898  * @constructor
19899  * @param {Object} config
19900  */
19901 Roo.data.JsonStore = function(c){
19902     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19903         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19904         reader: new Roo.data.JsonReader(c, c.fields)
19905     }));
19906 };
19907 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19908  * Based on:
19909  * Ext JS Library 1.1.1
19910  * Copyright(c) 2006-2007, Ext JS, LLC.
19911  *
19912  * Originally Released Under LGPL - original licence link has changed is not relivant.
19913  *
19914  * Fork - LGPL
19915  * <script type="text/javascript">
19916  */
19917
19918  
19919 Roo.data.Field = function(config){
19920     if(typeof config == "string"){
19921         config = {name: config};
19922     }
19923     Roo.apply(this, config);
19924     
19925     if(!this.type){
19926         this.type = "auto";
19927     }
19928     
19929     var st = Roo.data.SortTypes;
19930     // named sortTypes are supported, here we look them up
19931     if(typeof this.sortType == "string"){
19932         this.sortType = st[this.sortType];
19933     }
19934     
19935     // set default sortType for strings and dates
19936     if(!this.sortType){
19937         switch(this.type){
19938             case "string":
19939                 this.sortType = st.asUCString;
19940                 break;
19941             case "date":
19942                 this.sortType = st.asDate;
19943                 break;
19944             default:
19945                 this.sortType = st.none;
19946         }
19947     }
19948
19949     // define once
19950     var stripRe = /[\$,%]/g;
19951
19952     // prebuilt conversion function for this field, instead of
19953     // switching every time we're reading a value
19954     if(!this.convert){
19955         var cv, dateFormat = this.dateFormat;
19956         switch(this.type){
19957             case "":
19958             case "auto":
19959             case undefined:
19960                 cv = function(v){ return v; };
19961                 break;
19962             case "string":
19963                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19964                 break;
19965             case "int":
19966                 cv = function(v){
19967                     return v !== undefined && v !== null && v !== '' ?
19968                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19969                     };
19970                 break;
19971             case "float":
19972                 cv = function(v){
19973                     return v !== undefined && v !== null && v !== '' ?
19974                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19975                     };
19976                 break;
19977             case "bool":
19978             case "boolean":
19979                 cv = function(v){ return v === true || v === "true" || v == 1; };
19980                 break;
19981             case "date":
19982                 cv = function(v){
19983                     if(!v){
19984                         return '';
19985                     }
19986                     if(v instanceof Date){
19987                         return v;
19988                     }
19989                     if(dateFormat){
19990                         if(dateFormat == "timestamp"){
19991                             return new Date(v*1000);
19992                         }
19993                         return Date.parseDate(v, dateFormat);
19994                     }
19995                     var parsed = Date.parse(v);
19996                     return parsed ? new Date(parsed) : null;
19997                 };
19998              break;
19999             
20000         }
20001         this.convert = cv;
20002     }
20003 };
20004
20005 Roo.data.Field.prototype = {
20006     dateFormat: null,
20007     defaultValue: "",
20008     mapping: null,
20009     sortType : null,
20010     sortDir : "ASC"
20011 };/*
20012  * Based on:
20013  * Ext JS Library 1.1.1
20014  * Copyright(c) 2006-2007, Ext JS, LLC.
20015  *
20016  * Originally Released Under LGPL - original licence link has changed is not relivant.
20017  *
20018  * Fork - LGPL
20019  * <script type="text/javascript">
20020  */
20021  
20022 // Base class for reading structured data from a data source.  This class is intended to be
20023 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20024
20025 /**
20026  * @class Roo.data.DataReader
20027  * Base class for reading structured data from a data source.  This class is intended to be
20028  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20029  */
20030
20031 Roo.data.DataReader = function(meta, recordType){
20032     
20033     this.meta = meta;
20034     
20035     this.recordType = recordType instanceof Array ? 
20036         Roo.data.Record.create(recordType) : recordType;
20037 };
20038
20039 Roo.data.DataReader.prototype = {
20040      /**
20041      * Create an empty record
20042      * @param {Object} data (optional) - overlay some values
20043      * @return {Roo.data.Record} record created.
20044      */
20045     newRow :  function(d) {
20046         var da =  {};
20047         this.recordType.prototype.fields.each(function(c) {
20048             switch( c.type) {
20049                 case 'int' : da[c.name] = 0; break;
20050                 case 'date' : da[c.name] = new Date(); break;
20051                 case 'float' : da[c.name] = 0.0; break;
20052                 case 'boolean' : da[c.name] = false; break;
20053                 default : da[c.name] = ""; break;
20054             }
20055             
20056         });
20057         return new this.recordType(Roo.apply(da, d));
20058     }
20059     
20060 };/*
20061  * Based on:
20062  * Ext JS Library 1.1.1
20063  * Copyright(c) 2006-2007, Ext JS, LLC.
20064  *
20065  * Originally Released Under LGPL - original licence link has changed is not relivant.
20066  *
20067  * Fork - LGPL
20068  * <script type="text/javascript">
20069  */
20070
20071 /**
20072  * @class Roo.data.DataProxy
20073  * @extends Roo.data.Observable
20074  * This class is an abstract base class for implementations which provide retrieval of
20075  * unformatted data objects.<br>
20076  * <p>
20077  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20078  * (of the appropriate type which knows how to parse the data object) to provide a block of
20079  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20080  * <p>
20081  * Custom implementations must implement the load method as described in
20082  * {@link Roo.data.HttpProxy#load}.
20083  */
20084 Roo.data.DataProxy = function(){
20085     this.addEvents({
20086         /**
20087          * @event beforeload
20088          * Fires before a network request is made to retrieve a data object.
20089          * @param {Object} This DataProxy object.
20090          * @param {Object} params The params parameter to the load function.
20091          */
20092         beforeload : true,
20093         /**
20094          * @event load
20095          * Fires before the load method's callback is called.
20096          * @param {Object} This DataProxy object.
20097          * @param {Object} o The data object.
20098          * @param {Object} arg The callback argument object passed to the load function.
20099          */
20100         load : true,
20101         /**
20102          * @event loadexception
20103          * Fires if an Exception occurs during data retrieval.
20104          * @param {Object} This DataProxy object.
20105          * @param {Object} o The data object.
20106          * @param {Object} arg The callback argument object passed to the load function.
20107          * @param {Object} e The Exception.
20108          */
20109         loadexception : true
20110     });
20111     Roo.data.DataProxy.superclass.constructor.call(this);
20112 };
20113
20114 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20115
20116     /**
20117      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20118      */
20119 /*
20120  * Based on:
20121  * Ext JS Library 1.1.1
20122  * Copyright(c) 2006-2007, Ext JS, LLC.
20123  *
20124  * Originally Released Under LGPL - original licence link has changed is not relivant.
20125  *
20126  * Fork - LGPL
20127  * <script type="text/javascript">
20128  */
20129 /**
20130  * @class Roo.data.MemoryProxy
20131  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20132  * to the Reader when its load method is called.
20133  * @constructor
20134  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20135  */
20136 Roo.data.MemoryProxy = function(data){
20137     if (data.data) {
20138         data = data.data;
20139     }
20140     Roo.data.MemoryProxy.superclass.constructor.call(this);
20141     this.data = data;
20142 };
20143
20144 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20145     /**
20146      * Load data from the requested source (in this case an in-memory
20147      * data object passed to the constructor), read the data object into
20148      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20149      * process that block using the passed callback.
20150      * @param {Object} params This parameter is not used by the MemoryProxy class.
20151      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20152      * object into a block of Roo.data.Records.
20153      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20154      * The function must be passed <ul>
20155      * <li>The Record block object</li>
20156      * <li>The "arg" argument from the load function</li>
20157      * <li>A boolean success indicator</li>
20158      * </ul>
20159      * @param {Object} scope The scope in which to call the callback
20160      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20161      */
20162     load : function(params, reader, callback, scope, arg){
20163         params = params || {};
20164         var result;
20165         try {
20166             result = reader.readRecords(this.data);
20167         }catch(e){
20168             this.fireEvent("loadexception", this, arg, null, e);
20169             callback.call(scope, null, arg, false);
20170             return;
20171         }
20172         callback.call(scope, result, arg, true);
20173     },
20174     
20175     // private
20176     update : function(params, records){
20177         
20178     }
20179 });/*
20180  * Based on:
20181  * Ext JS Library 1.1.1
20182  * Copyright(c) 2006-2007, Ext JS, LLC.
20183  *
20184  * Originally Released Under LGPL - original licence link has changed is not relivant.
20185  *
20186  * Fork - LGPL
20187  * <script type="text/javascript">
20188  */
20189 /**
20190  * @class Roo.data.HttpProxy
20191  * @extends Roo.data.DataProxy
20192  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20193  * configured to reference a certain URL.<br><br>
20194  * <p>
20195  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20196  * from which the running page was served.<br><br>
20197  * <p>
20198  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20199  * <p>
20200  * Be aware that to enable the browser to parse an XML document, the server must set
20201  * the Content-Type header in the HTTP response to "text/xml".
20202  * @constructor
20203  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20204  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20205  * will be used to make the request.
20206  */
20207 Roo.data.HttpProxy = function(conn){
20208     Roo.data.HttpProxy.superclass.constructor.call(this);
20209     // is conn a conn config or a real conn?
20210     this.conn = conn;
20211     this.useAjax = !conn || !conn.events;
20212   
20213 };
20214
20215 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20216     // thse are take from connection...
20217     
20218     /**
20219      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20220      */
20221     /**
20222      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20223      * extra parameters to each request made by this object. (defaults to undefined)
20224      */
20225     /**
20226      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20227      *  to each request made by this object. (defaults to undefined)
20228      */
20229     /**
20230      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20231      */
20232     /**
20233      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20234      */
20235      /**
20236      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20237      * @type Boolean
20238      */
20239   
20240
20241     /**
20242      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20243      * @type Boolean
20244      */
20245     /**
20246      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20247      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20248      * a finer-grained basis than the DataProxy events.
20249      */
20250     getConnection : function(){
20251         return this.useAjax ? Roo.Ajax : this.conn;
20252     },
20253
20254     /**
20255      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20256      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20257      * process that block using the passed callback.
20258      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20259      * for the request to the remote server.
20260      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20261      * object into a block of Roo.data.Records.
20262      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20263      * The function must be passed <ul>
20264      * <li>The Record block object</li>
20265      * <li>The "arg" argument from the load function</li>
20266      * <li>A boolean success indicator</li>
20267      * </ul>
20268      * @param {Object} scope The scope in which to call the callback
20269      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20270      */
20271     load : function(params, reader, callback, scope, arg){
20272         if(this.fireEvent("beforeload", this, params) !== false){
20273             var  o = {
20274                 params : params || {},
20275                 request: {
20276                     callback : callback,
20277                     scope : scope,
20278                     arg : arg
20279                 },
20280                 reader: reader,
20281                 callback : this.loadResponse,
20282                 scope: this
20283             };
20284             if(this.useAjax){
20285                 Roo.applyIf(o, this.conn);
20286                 if(this.activeRequest){
20287                     Roo.Ajax.abort(this.activeRequest);
20288                 }
20289                 this.activeRequest = Roo.Ajax.request(o);
20290             }else{
20291                 this.conn.request(o);
20292             }
20293         }else{
20294             callback.call(scope||this, null, arg, false);
20295         }
20296     },
20297
20298     // private
20299     loadResponse : function(o, success, response){
20300         delete this.activeRequest;
20301         if(!success){
20302             this.fireEvent("loadexception", this, o, response);
20303             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20304             return;
20305         }
20306         var result;
20307         try {
20308             result = o.reader.read(response);
20309         }catch(e){
20310             this.fireEvent("loadexception", this, o, response, e);
20311             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20312             return;
20313         }
20314         
20315         this.fireEvent("load", this, o, o.request.arg);
20316         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20317     },
20318
20319     // private
20320     update : function(dataSet){
20321
20322     },
20323
20324     // private
20325     updateResponse : function(dataSet){
20326
20327     }
20328 });/*
20329  * Based on:
20330  * Ext JS Library 1.1.1
20331  * Copyright(c) 2006-2007, Ext JS, LLC.
20332  *
20333  * Originally Released Under LGPL - original licence link has changed is not relivant.
20334  *
20335  * Fork - LGPL
20336  * <script type="text/javascript">
20337  */
20338
20339 /**
20340  * @class Roo.data.ScriptTagProxy
20341  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20342  * other than the originating domain of the running page.<br><br>
20343  * <p>
20344  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20345  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20346  * <p>
20347  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20348  * source code that is used as the source inside a &lt;script> tag.<br><br>
20349  * <p>
20350  * In order for the browser to process the returned data, the server must wrap the data object
20351  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20352  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20353  * depending on whether the callback name was passed:
20354  * <p>
20355  * <pre><code>
20356 boolean scriptTag = false;
20357 String cb = request.getParameter("callback");
20358 if (cb != null) {
20359     scriptTag = true;
20360     response.setContentType("text/javascript");
20361 } else {
20362     response.setContentType("application/x-json");
20363 }
20364 Writer out = response.getWriter();
20365 if (scriptTag) {
20366     out.write(cb + "(");
20367 }
20368 out.print(dataBlock.toJsonString());
20369 if (scriptTag) {
20370     out.write(");");
20371 }
20372 </pre></code>
20373  *
20374  * @constructor
20375  * @param {Object} config A configuration object.
20376  */
20377 Roo.data.ScriptTagProxy = function(config){
20378     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20379     Roo.apply(this, config);
20380     this.head = document.getElementsByTagName("head")[0];
20381 };
20382
20383 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20384
20385 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20386     /**
20387      * @cfg {String} url The URL from which to request the data object.
20388      */
20389     /**
20390      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20391      */
20392     timeout : 30000,
20393     /**
20394      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20395      * the server the name of the callback function set up by the load call to process the returned data object.
20396      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20397      * javascript output which calls this named function passing the data object as its only parameter.
20398      */
20399     callbackParam : "callback",
20400     /**
20401      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20402      * name to the request.
20403      */
20404     nocache : true,
20405
20406     /**
20407      * Load data from the configured URL, read the data object into
20408      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20409      * process that block using the passed callback.
20410      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20411      * for the request to the remote server.
20412      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20413      * object into a block of Roo.data.Records.
20414      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20415      * The function must be passed <ul>
20416      * <li>The Record block object</li>
20417      * <li>The "arg" argument from the load function</li>
20418      * <li>A boolean success indicator</li>
20419      * </ul>
20420      * @param {Object} scope The scope in which to call the callback
20421      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20422      */
20423     load : function(params, reader, callback, scope, arg){
20424         if(this.fireEvent("beforeload", this, params) !== false){
20425
20426             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20427
20428             var url = this.url;
20429             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20430             if(this.nocache){
20431                 url += "&_dc=" + (new Date().getTime());
20432             }
20433             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20434             var trans = {
20435                 id : transId,
20436                 cb : "stcCallback"+transId,
20437                 scriptId : "stcScript"+transId,
20438                 params : params,
20439                 arg : arg,
20440                 url : url,
20441                 callback : callback,
20442                 scope : scope,
20443                 reader : reader
20444             };
20445             var conn = this;
20446
20447             window[trans.cb] = function(o){
20448                 conn.handleResponse(o, trans);
20449             };
20450
20451             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20452
20453             if(this.autoAbort !== false){
20454                 this.abort();
20455             }
20456
20457             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20458
20459             var script = document.createElement("script");
20460             script.setAttribute("src", url);
20461             script.setAttribute("type", "text/javascript");
20462             script.setAttribute("id", trans.scriptId);
20463             this.head.appendChild(script);
20464
20465             this.trans = trans;
20466         }else{
20467             callback.call(scope||this, null, arg, false);
20468         }
20469     },
20470
20471     // private
20472     isLoading : function(){
20473         return this.trans ? true : false;
20474     },
20475
20476     /**
20477      * Abort the current server request.
20478      */
20479     abort : function(){
20480         if(this.isLoading()){
20481             this.destroyTrans(this.trans);
20482         }
20483     },
20484
20485     // private
20486     destroyTrans : function(trans, isLoaded){
20487         this.head.removeChild(document.getElementById(trans.scriptId));
20488         clearTimeout(trans.timeoutId);
20489         if(isLoaded){
20490             window[trans.cb] = undefined;
20491             try{
20492                 delete window[trans.cb];
20493             }catch(e){}
20494         }else{
20495             // if hasn't been loaded, wait for load to remove it to prevent script error
20496             window[trans.cb] = function(){
20497                 window[trans.cb] = undefined;
20498                 try{
20499                     delete window[trans.cb];
20500                 }catch(e){}
20501             };
20502         }
20503     },
20504
20505     // private
20506     handleResponse : function(o, trans){
20507         this.trans = false;
20508         this.destroyTrans(trans, true);
20509         var result;
20510         try {
20511             result = trans.reader.readRecords(o);
20512         }catch(e){
20513             this.fireEvent("loadexception", this, o, trans.arg, e);
20514             trans.callback.call(trans.scope||window, null, trans.arg, false);
20515             return;
20516         }
20517         this.fireEvent("load", this, o, trans.arg);
20518         trans.callback.call(trans.scope||window, result, trans.arg, true);
20519     },
20520
20521     // private
20522     handleFailure : function(trans){
20523         this.trans = false;
20524         this.destroyTrans(trans, false);
20525         this.fireEvent("loadexception", this, null, trans.arg);
20526         trans.callback.call(trans.scope||window, null, trans.arg, false);
20527     }
20528 });/*
20529  * Based on:
20530  * Ext JS Library 1.1.1
20531  * Copyright(c) 2006-2007, Ext JS, LLC.
20532  *
20533  * Originally Released Under LGPL - original licence link has changed is not relivant.
20534  *
20535  * Fork - LGPL
20536  * <script type="text/javascript">
20537  */
20538
20539 /**
20540  * @class Roo.data.JsonReader
20541  * @extends Roo.data.DataReader
20542  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20543  * based on mappings in a provided Roo.data.Record constructor.
20544  * 
20545  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20546  * in the reply previously. 
20547  * 
20548  * <p>
20549  * Example code:
20550  * <pre><code>
20551 var RecordDef = Roo.data.Record.create([
20552     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20553     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20554 ]);
20555 var myReader = new Roo.data.JsonReader({
20556     totalProperty: "results",    // The property which contains the total dataset size (optional)
20557     root: "rows",                // The property which contains an Array of row objects
20558     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20559 }, RecordDef);
20560 </code></pre>
20561  * <p>
20562  * This would consume a JSON file like this:
20563  * <pre><code>
20564 { 'results': 2, 'rows': [
20565     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20566     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20567 }
20568 </code></pre>
20569  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20570  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20571  * paged from the remote server.
20572  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20573  * @cfg {String} root name of the property which contains the Array of row objects.
20574  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20575  * @constructor
20576  * Create a new JsonReader
20577  * @param {Object} meta Metadata configuration options
20578  * @param {Object} recordType Either an Array of field definition objects,
20579  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20580  */
20581 Roo.data.JsonReader = function(meta, recordType){
20582     
20583     meta = meta || {};
20584     // set some defaults:
20585     Roo.applyIf(meta, {
20586         totalProperty: 'total',
20587         successProperty : 'success',
20588         root : 'data',
20589         id : 'id'
20590     });
20591     
20592     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20593 };
20594 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20595     
20596     /**
20597      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20598      * Used by Store query builder to append _requestMeta to params.
20599      * 
20600      */
20601     metaFromRemote : false,
20602     /**
20603      * This method is only used by a DataProxy which has retrieved data from a remote server.
20604      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20605      * @return {Object} data A data block which is used by an Roo.data.Store object as
20606      * a cache of Roo.data.Records.
20607      */
20608     read : function(response){
20609         var json = response.responseText;
20610        
20611         var o = /* eval:var:o */ eval("("+json+")");
20612         if(!o) {
20613             throw {message: "JsonReader.read: Json object not found"};
20614         }
20615         
20616         if(o.metaData){
20617             
20618             delete this.ef;
20619             this.metaFromRemote = true;
20620             this.meta = o.metaData;
20621             this.recordType = Roo.data.Record.create(o.metaData.fields);
20622             this.onMetaChange(this.meta, this.recordType, o);
20623         }
20624         return this.readRecords(o);
20625     },
20626
20627     // private function a store will implement
20628     onMetaChange : function(meta, recordType, o){
20629
20630     },
20631
20632     /**
20633          * @ignore
20634          */
20635     simpleAccess: function(obj, subsc) {
20636         return obj[subsc];
20637     },
20638
20639         /**
20640          * @ignore
20641          */
20642     getJsonAccessor: function(){
20643         var re = /[\[\.]/;
20644         return function(expr) {
20645             try {
20646                 return(re.test(expr))
20647                     ? new Function("obj", "return obj." + expr)
20648                     : function(obj){
20649                         return obj[expr];
20650                     };
20651             } catch(e){}
20652             return Roo.emptyFn;
20653         };
20654     }(),
20655
20656     /**
20657      * Create a data block containing Roo.data.Records from an XML document.
20658      * @param {Object} o An object which contains an Array of row objects in the property specified
20659      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20660      * which contains the total size of the dataset.
20661      * @return {Object} data A data block which is used by an Roo.data.Store object as
20662      * a cache of Roo.data.Records.
20663      */
20664     readRecords : function(o){
20665         /**
20666          * After any data loads, the raw JSON data is available for further custom processing.
20667          * @type Object
20668          */
20669         this.jsonData = o;
20670         var s = this.meta, Record = this.recordType,
20671             f = Record.prototype.fields, fi = f.items, fl = f.length;
20672
20673 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20674         if (!this.ef) {
20675             if(s.totalProperty) {
20676                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20677                 }
20678                 if(s.successProperty) {
20679                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20680                 }
20681                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20682                 if (s.id) {
20683                         var g = this.getJsonAccessor(s.id);
20684                         this.getId = function(rec) {
20685                                 var r = g(rec);
20686                                 return (r === undefined || r === "") ? null : r;
20687                         };
20688                 } else {
20689                         this.getId = function(){return null;};
20690                 }
20691             this.ef = [];
20692             for(var jj = 0; jj < fl; jj++){
20693                 f = fi[jj];
20694                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20695                 this.ef[jj] = this.getJsonAccessor(map);
20696             }
20697         }
20698
20699         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20700         if(s.totalProperty){
20701             var vt = parseInt(this.getTotal(o), 10);
20702             if(!isNaN(vt)){
20703                 totalRecords = vt;
20704             }
20705         }
20706         if(s.successProperty){
20707             var vs = this.getSuccess(o);
20708             if(vs === false || vs === 'false'){
20709                 success = false;
20710             }
20711         }
20712         var records = [];
20713             for(var i = 0; i < c; i++){
20714                     var n = root[i];
20715                 var values = {};
20716                 var id = this.getId(n);
20717                 for(var j = 0; j < fl; j++){
20718                     f = fi[j];
20719                 var v = this.ef[j](n);
20720                 if (!f.convert) {
20721                     Roo.log('missing convert for ' + f.name);
20722                     Roo.log(f);
20723                     continue;
20724                 }
20725                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20726                 }
20727                 var record = new Record(values, id);
20728                 record.json = n;
20729                 records[i] = record;
20730             }
20731             return {
20732                 success : success,
20733                 records : records,
20734                 totalRecords : totalRecords
20735             };
20736     }
20737 });/*
20738  * Based on:
20739  * Ext JS Library 1.1.1
20740  * Copyright(c) 2006-2007, Ext JS, LLC.
20741  *
20742  * Originally Released Under LGPL - original licence link has changed is not relivant.
20743  *
20744  * Fork - LGPL
20745  * <script type="text/javascript">
20746  */
20747
20748 /**
20749  * @class Roo.data.XmlReader
20750  * @extends Roo.data.DataReader
20751  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20752  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20753  * <p>
20754  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20755  * header in the HTTP response must be set to "text/xml".</em>
20756  * <p>
20757  * Example code:
20758  * <pre><code>
20759 var RecordDef = Roo.data.Record.create([
20760    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20761    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20762 ]);
20763 var myReader = new Roo.data.XmlReader({
20764    totalRecords: "results", // The element which contains the total dataset size (optional)
20765    record: "row",           // The repeated element which contains row information
20766    id: "id"                 // The element within the row that provides an ID for the record (optional)
20767 }, RecordDef);
20768 </code></pre>
20769  * <p>
20770  * This would consume an XML file like this:
20771  * <pre><code>
20772 &lt;?xml?>
20773 &lt;dataset>
20774  &lt;results>2&lt;/results>
20775  &lt;row>
20776    &lt;id>1&lt;/id>
20777    &lt;name>Bill&lt;/name>
20778    &lt;occupation>Gardener&lt;/occupation>
20779  &lt;/row>
20780  &lt;row>
20781    &lt;id>2&lt;/id>
20782    &lt;name>Ben&lt;/name>
20783    &lt;occupation>Horticulturalist&lt;/occupation>
20784  &lt;/row>
20785 &lt;/dataset>
20786 </code></pre>
20787  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20788  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20789  * paged from the remote server.
20790  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20791  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20792  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20793  * a record identifier value.
20794  * @constructor
20795  * Create a new XmlReader
20796  * @param {Object} meta Metadata configuration options
20797  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20798  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20799  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20800  */
20801 Roo.data.XmlReader = function(meta, recordType){
20802     meta = meta || {};
20803     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20804 };
20805 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20806     /**
20807      * This method is only used by a DataProxy which has retrieved data from a remote server.
20808          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20809          * to contain a method called 'responseXML' that returns an XML document object.
20810      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20811      * a cache of Roo.data.Records.
20812      */
20813     read : function(response){
20814         var doc = response.responseXML;
20815         if(!doc) {
20816             throw {message: "XmlReader.read: XML Document not available"};
20817         }
20818         return this.readRecords(doc);
20819     },
20820
20821     /**
20822      * Create a data block containing Roo.data.Records from an XML document.
20823          * @param {Object} doc A parsed XML document.
20824      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20825      * a cache of Roo.data.Records.
20826      */
20827     readRecords : function(doc){
20828         /**
20829          * After any data loads/reads, the raw XML Document is available for further custom processing.
20830          * @type XMLDocument
20831          */
20832         this.xmlData = doc;
20833         var root = doc.documentElement || doc;
20834         var q = Roo.DomQuery;
20835         var recordType = this.recordType, fields = recordType.prototype.fields;
20836         var sid = this.meta.id;
20837         var totalRecords = 0, success = true;
20838         if(this.meta.totalRecords){
20839             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20840         }
20841         
20842         if(this.meta.success){
20843             var sv = q.selectValue(this.meta.success, root, true);
20844             success = sv !== false && sv !== 'false';
20845         }
20846         var records = [];
20847         var ns = q.select(this.meta.record, root);
20848         for(var i = 0, len = ns.length; i < len; i++) {
20849                 var n = ns[i];
20850                 var values = {};
20851                 var id = sid ? q.selectValue(sid, n) : undefined;
20852                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20853                     var f = fields.items[j];
20854                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20855                     v = f.convert(v);
20856                     values[f.name] = v;
20857                 }
20858                 var record = new recordType(values, id);
20859                 record.node = n;
20860                 records[records.length] = record;
20861             }
20862
20863             return {
20864                 success : success,
20865                 records : records,
20866                 totalRecords : totalRecords || records.length
20867             };
20868     }
20869 });/*
20870  * Based on:
20871  * Ext JS Library 1.1.1
20872  * Copyright(c) 2006-2007, Ext JS, LLC.
20873  *
20874  * Originally Released Under LGPL - original licence link has changed is not relivant.
20875  *
20876  * Fork - LGPL
20877  * <script type="text/javascript">
20878  */
20879
20880 /**
20881  * @class Roo.data.ArrayReader
20882  * @extends Roo.data.DataReader
20883  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20884  * Each element of that Array represents a row of data fields. The
20885  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20886  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20887  * <p>
20888  * Example code:.
20889  * <pre><code>
20890 var RecordDef = Roo.data.Record.create([
20891     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20892     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20893 ]);
20894 var myReader = new Roo.data.ArrayReader({
20895     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20896 }, RecordDef);
20897 </code></pre>
20898  * <p>
20899  * This would consume an Array like this:
20900  * <pre><code>
20901 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20902   </code></pre>
20903  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20904  * @constructor
20905  * Create a new JsonReader
20906  * @param {Object} meta Metadata configuration options.
20907  * @param {Object} recordType Either an Array of field definition objects
20908  * as specified to {@link Roo.data.Record#create},
20909  * or an {@link Roo.data.Record} object
20910  * created using {@link Roo.data.Record#create}.
20911  */
20912 Roo.data.ArrayReader = function(meta, recordType){
20913     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20914 };
20915
20916 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20917     /**
20918      * Create a data block containing Roo.data.Records from an XML document.
20919      * @param {Object} o An Array of row objects which represents the dataset.
20920      * @return {Object} data A data block which is used by an Roo.data.Store object as
20921      * a cache of Roo.data.Records.
20922      */
20923     readRecords : function(o){
20924         var sid = this.meta ? this.meta.id : null;
20925         var recordType = this.recordType, fields = recordType.prototype.fields;
20926         var records = [];
20927         var root = o;
20928             for(var i = 0; i < root.length; i++){
20929                     var n = root[i];
20930                 var values = {};
20931                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20932                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20933                 var f = fields.items[j];
20934                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20935                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20936                 v = f.convert(v);
20937                 values[f.name] = v;
20938             }
20939                 var record = new recordType(values, id);
20940                 record.json = n;
20941                 records[records.length] = record;
20942             }
20943             return {
20944                 records : records,
20945                 totalRecords : records.length
20946             };
20947     }
20948 });/*
20949  * Based on:
20950  * Ext JS Library 1.1.1
20951  * Copyright(c) 2006-2007, Ext JS, LLC.
20952  *
20953  * Originally Released Under LGPL - original licence link has changed is not relivant.
20954  *
20955  * Fork - LGPL
20956  * <script type="text/javascript">
20957  */
20958
20959
20960 /**
20961  * @class Roo.data.Tree
20962  * @extends Roo.util.Observable
20963  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20964  * in the tree have most standard DOM functionality.
20965  * @constructor
20966  * @param {Node} root (optional) The root node
20967  */
20968 Roo.data.Tree = function(root){
20969    this.nodeHash = {};
20970    /**
20971     * The root node for this tree
20972     * @type Node
20973     */
20974    this.root = null;
20975    if(root){
20976        this.setRootNode(root);
20977    }
20978    this.addEvents({
20979        /**
20980         * @event append
20981         * Fires when a new child node is appended to a node in this tree.
20982         * @param {Tree} tree The owner tree
20983         * @param {Node} parent The parent node
20984         * @param {Node} node The newly appended node
20985         * @param {Number} index The index of the newly appended node
20986         */
20987        "append" : true,
20988        /**
20989         * @event remove
20990         * Fires when a child node is removed from a node in this tree.
20991         * @param {Tree} tree The owner tree
20992         * @param {Node} parent The parent node
20993         * @param {Node} node The child node removed
20994         */
20995        "remove" : true,
20996        /**
20997         * @event move
20998         * Fires when a node is moved to a new location in the tree
20999         * @param {Tree} tree The owner tree
21000         * @param {Node} node The node moved
21001         * @param {Node} oldParent The old parent of this node
21002         * @param {Node} newParent The new parent of this node
21003         * @param {Number} index The index it was moved to
21004         */
21005        "move" : true,
21006        /**
21007         * @event insert
21008         * Fires when a new child node is inserted in a node in this tree.
21009         * @param {Tree} tree The owner tree
21010         * @param {Node} parent The parent node
21011         * @param {Node} node The child node inserted
21012         * @param {Node} refNode The child node the node was inserted before
21013         */
21014        "insert" : true,
21015        /**
21016         * @event beforeappend
21017         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21018         * @param {Tree} tree The owner tree
21019         * @param {Node} parent The parent node
21020         * @param {Node} node The child node to be appended
21021         */
21022        "beforeappend" : true,
21023        /**
21024         * @event beforeremove
21025         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21026         * @param {Tree} tree The owner tree
21027         * @param {Node} parent The parent node
21028         * @param {Node} node The child node to be removed
21029         */
21030        "beforeremove" : true,
21031        /**
21032         * @event beforemove
21033         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21034         * @param {Tree} tree The owner tree
21035         * @param {Node} node The node being moved
21036         * @param {Node} oldParent The parent of the node
21037         * @param {Node} newParent The new parent the node is moving to
21038         * @param {Number} index The index it is being moved to
21039         */
21040        "beforemove" : true,
21041        /**
21042         * @event beforeinsert
21043         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21044         * @param {Tree} tree The owner tree
21045         * @param {Node} parent The parent node
21046         * @param {Node} node The child node to be inserted
21047         * @param {Node} refNode The child node the node is being inserted before
21048         */
21049        "beforeinsert" : true
21050    });
21051
21052     Roo.data.Tree.superclass.constructor.call(this);
21053 };
21054
21055 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21056     pathSeparator: "/",
21057
21058     proxyNodeEvent : function(){
21059         return this.fireEvent.apply(this, arguments);
21060     },
21061
21062     /**
21063      * Returns the root node for this tree.
21064      * @return {Node}
21065      */
21066     getRootNode : function(){
21067         return this.root;
21068     },
21069
21070     /**
21071      * Sets the root node for this tree.
21072      * @param {Node} node
21073      * @return {Node}
21074      */
21075     setRootNode : function(node){
21076         this.root = node;
21077         node.ownerTree = this;
21078         node.isRoot = true;
21079         this.registerNode(node);
21080         return node;
21081     },
21082
21083     /**
21084      * Gets a node in this tree by its id.
21085      * @param {String} id
21086      * @return {Node}
21087      */
21088     getNodeById : function(id){
21089         return this.nodeHash[id];
21090     },
21091
21092     registerNode : function(node){
21093         this.nodeHash[node.id] = node;
21094     },
21095
21096     unregisterNode : function(node){
21097         delete this.nodeHash[node.id];
21098     },
21099
21100     toString : function(){
21101         return "[Tree"+(this.id?" "+this.id:"")+"]";
21102     }
21103 });
21104
21105 /**
21106  * @class Roo.data.Node
21107  * @extends Roo.util.Observable
21108  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21109  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21110  * @constructor
21111  * @param {Object} attributes The attributes/config for the node
21112  */
21113 Roo.data.Node = function(attributes){
21114     /**
21115      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21116      * @type {Object}
21117      */
21118     this.attributes = attributes || {};
21119     this.leaf = this.attributes.leaf;
21120     /**
21121      * The node id. @type String
21122      */
21123     this.id = this.attributes.id;
21124     if(!this.id){
21125         this.id = Roo.id(null, "ynode-");
21126         this.attributes.id = this.id;
21127     }
21128     /**
21129      * All child nodes of this node. @type Array
21130      */
21131     this.childNodes = [];
21132     if(!this.childNodes.indexOf){ // indexOf is a must
21133         this.childNodes.indexOf = function(o){
21134             for(var i = 0, len = this.length; i < len; i++){
21135                 if(this[i] == o) {
21136                     return i;
21137                 }
21138             }
21139             return -1;
21140         };
21141     }
21142     /**
21143      * The parent node for this node. @type Node
21144      */
21145     this.parentNode = null;
21146     /**
21147      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21148      */
21149     this.firstChild = null;
21150     /**
21151      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21152      */
21153     this.lastChild = null;
21154     /**
21155      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21156      */
21157     this.previousSibling = null;
21158     /**
21159      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21160      */
21161     this.nextSibling = null;
21162
21163     this.addEvents({
21164        /**
21165         * @event append
21166         * Fires when a new child node is appended
21167         * @param {Tree} tree The owner tree
21168         * @param {Node} this This node
21169         * @param {Node} node The newly appended node
21170         * @param {Number} index The index of the newly appended node
21171         */
21172        "append" : true,
21173        /**
21174         * @event remove
21175         * Fires when a child node is removed
21176         * @param {Tree} tree The owner tree
21177         * @param {Node} this This node
21178         * @param {Node} node The removed node
21179         */
21180        "remove" : true,
21181        /**
21182         * @event move
21183         * Fires when this node is moved to a new location in the tree
21184         * @param {Tree} tree The owner tree
21185         * @param {Node} this This node
21186         * @param {Node} oldParent The old parent of this node
21187         * @param {Node} newParent The new parent of this node
21188         * @param {Number} index The index it was moved to
21189         */
21190        "move" : true,
21191        /**
21192         * @event insert
21193         * Fires when a new child node is inserted.
21194         * @param {Tree} tree The owner tree
21195         * @param {Node} this This node
21196         * @param {Node} node The child node inserted
21197         * @param {Node} refNode The child node the node was inserted before
21198         */
21199        "insert" : true,
21200        /**
21201         * @event beforeappend
21202         * Fires before a new child is appended, return false to cancel the append.
21203         * @param {Tree} tree The owner tree
21204         * @param {Node} this This node
21205         * @param {Node} node The child node to be appended
21206         */
21207        "beforeappend" : true,
21208        /**
21209         * @event beforeremove
21210         * Fires before a child is removed, return false to cancel the remove.
21211         * @param {Tree} tree The owner tree
21212         * @param {Node} this This node
21213         * @param {Node} node The child node to be removed
21214         */
21215        "beforeremove" : true,
21216        /**
21217         * @event beforemove
21218         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21219         * @param {Tree} tree The owner tree
21220         * @param {Node} this This node
21221         * @param {Node} oldParent The parent of this node
21222         * @param {Node} newParent The new parent this node is moving to
21223         * @param {Number} index The index it is being moved to
21224         */
21225        "beforemove" : true,
21226        /**
21227         * @event beforeinsert
21228         * Fires before a new child is inserted, return false to cancel the insert.
21229         * @param {Tree} tree The owner tree
21230         * @param {Node} this This node
21231         * @param {Node} node The child node to be inserted
21232         * @param {Node} refNode The child node the node is being inserted before
21233         */
21234        "beforeinsert" : true
21235    });
21236     this.listeners = this.attributes.listeners;
21237     Roo.data.Node.superclass.constructor.call(this);
21238 };
21239
21240 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21241     fireEvent : function(evtName){
21242         // first do standard event for this node
21243         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21244             return false;
21245         }
21246         // then bubble it up to the tree if the event wasn't cancelled
21247         var ot = this.getOwnerTree();
21248         if(ot){
21249             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21250                 return false;
21251             }
21252         }
21253         return true;
21254     },
21255
21256     /**
21257      * Returns true if this node is a leaf
21258      * @return {Boolean}
21259      */
21260     isLeaf : function(){
21261         return this.leaf === true;
21262     },
21263
21264     // private
21265     setFirstChild : function(node){
21266         this.firstChild = node;
21267     },
21268
21269     //private
21270     setLastChild : function(node){
21271         this.lastChild = node;
21272     },
21273
21274
21275     /**
21276      * Returns true if this node is the last child of its parent
21277      * @return {Boolean}
21278      */
21279     isLast : function(){
21280        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21281     },
21282
21283     /**
21284      * Returns true if this node is the first child of its parent
21285      * @return {Boolean}
21286      */
21287     isFirst : function(){
21288        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21289     },
21290
21291     hasChildNodes : function(){
21292         return !this.isLeaf() && this.childNodes.length > 0;
21293     },
21294
21295     /**
21296      * Insert node(s) as the last child node of this node.
21297      * @param {Node/Array} node The node or Array of nodes to append
21298      * @return {Node} The appended node if single append, or null if an array was passed
21299      */
21300     appendChild : function(node){
21301         var multi = false;
21302         if(node instanceof Array){
21303             multi = node;
21304         }else if(arguments.length > 1){
21305             multi = arguments;
21306         }
21307         // if passed an array or multiple args do them one by one
21308         if(multi){
21309             for(var i = 0, len = multi.length; i < len; i++) {
21310                 this.appendChild(multi[i]);
21311             }
21312         }else{
21313             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21314                 return false;
21315             }
21316             var index = this.childNodes.length;
21317             var oldParent = node.parentNode;
21318             // it's a move, make sure we move it cleanly
21319             if(oldParent){
21320                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21321                     return false;
21322                 }
21323                 oldParent.removeChild(node);
21324             }
21325             index = this.childNodes.length;
21326             if(index == 0){
21327                 this.setFirstChild(node);
21328             }
21329             this.childNodes.push(node);
21330             node.parentNode = this;
21331             var ps = this.childNodes[index-1];
21332             if(ps){
21333                 node.previousSibling = ps;
21334                 ps.nextSibling = node;
21335             }else{
21336                 node.previousSibling = null;
21337             }
21338             node.nextSibling = null;
21339             this.setLastChild(node);
21340             node.setOwnerTree(this.getOwnerTree());
21341             this.fireEvent("append", this.ownerTree, this, node, index);
21342             if(oldParent){
21343                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21344             }
21345             return node;
21346         }
21347     },
21348
21349     /**
21350      * Removes a child node from this node.
21351      * @param {Node} node The node to remove
21352      * @return {Node} The removed node
21353      */
21354     removeChild : function(node){
21355         var index = this.childNodes.indexOf(node);
21356         if(index == -1){
21357             return false;
21358         }
21359         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21360             return false;
21361         }
21362
21363         // remove it from childNodes collection
21364         this.childNodes.splice(index, 1);
21365
21366         // update siblings
21367         if(node.previousSibling){
21368             node.previousSibling.nextSibling = node.nextSibling;
21369         }
21370         if(node.nextSibling){
21371             node.nextSibling.previousSibling = node.previousSibling;
21372         }
21373
21374         // update child refs
21375         if(this.firstChild == node){
21376             this.setFirstChild(node.nextSibling);
21377         }
21378         if(this.lastChild == node){
21379             this.setLastChild(node.previousSibling);
21380         }
21381
21382         node.setOwnerTree(null);
21383         // clear any references from the node
21384         node.parentNode = null;
21385         node.previousSibling = null;
21386         node.nextSibling = null;
21387         this.fireEvent("remove", this.ownerTree, this, node);
21388         return node;
21389     },
21390
21391     /**
21392      * Inserts the first node before the second node in this nodes childNodes collection.
21393      * @param {Node} node The node to insert
21394      * @param {Node} refNode The node to insert before (if null the node is appended)
21395      * @return {Node} The inserted node
21396      */
21397     insertBefore : function(node, refNode){
21398         if(!refNode){ // like standard Dom, refNode can be null for append
21399             return this.appendChild(node);
21400         }
21401         // nothing to do
21402         if(node == refNode){
21403             return false;
21404         }
21405
21406         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21407             return false;
21408         }
21409         var index = this.childNodes.indexOf(refNode);
21410         var oldParent = node.parentNode;
21411         var refIndex = index;
21412
21413         // when moving internally, indexes will change after remove
21414         if(oldParent == this && this.childNodes.indexOf(node) < index){
21415             refIndex--;
21416         }
21417
21418         // it's a move, make sure we move it cleanly
21419         if(oldParent){
21420             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21421                 return false;
21422             }
21423             oldParent.removeChild(node);
21424         }
21425         if(refIndex == 0){
21426             this.setFirstChild(node);
21427         }
21428         this.childNodes.splice(refIndex, 0, node);
21429         node.parentNode = this;
21430         var ps = this.childNodes[refIndex-1];
21431         if(ps){
21432             node.previousSibling = ps;
21433             ps.nextSibling = node;
21434         }else{
21435             node.previousSibling = null;
21436         }
21437         node.nextSibling = refNode;
21438         refNode.previousSibling = node;
21439         node.setOwnerTree(this.getOwnerTree());
21440         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21441         if(oldParent){
21442             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21443         }
21444         return node;
21445     },
21446
21447     /**
21448      * Returns the child node at the specified index.
21449      * @param {Number} index
21450      * @return {Node}
21451      */
21452     item : function(index){
21453         return this.childNodes[index];
21454     },
21455
21456     /**
21457      * Replaces one child node in this node with another.
21458      * @param {Node} newChild The replacement node
21459      * @param {Node} oldChild The node to replace
21460      * @return {Node} The replaced node
21461      */
21462     replaceChild : function(newChild, oldChild){
21463         this.insertBefore(newChild, oldChild);
21464         this.removeChild(oldChild);
21465         return oldChild;
21466     },
21467
21468     /**
21469      * Returns the index of a child node
21470      * @param {Node} node
21471      * @return {Number} The index of the node or -1 if it was not found
21472      */
21473     indexOf : function(child){
21474         return this.childNodes.indexOf(child);
21475     },
21476
21477     /**
21478      * Returns the tree this node is in.
21479      * @return {Tree}
21480      */
21481     getOwnerTree : function(){
21482         // if it doesn't have one, look for one
21483         if(!this.ownerTree){
21484             var p = this;
21485             while(p){
21486                 if(p.ownerTree){
21487                     this.ownerTree = p.ownerTree;
21488                     break;
21489                 }
21490                 p = p.parentNode;
21491             }
21492         }
21493         return this.ownerTree;
21494     },
21495
21496     /**
21497      * Returns depth of this node (the root node has a depth of 0)
21498      * @return {Number}
21499      */
21500     getDepth : function(){
21501         var depth = 0;
21502         var p = this;
21503         while(p.parentNode){
21504             ++depth;
21505             p = p.parentNode;
21506         }
21507         return depth;
21508     },
21509
21510     // private
21511     setOwnerTree : function(tree){
21512         // if it's move, we need to update everyone
21513         if(tree != this.ownerTree){
21514             if(this.ownerTree){
21515                 this.ownerTree.unregisterNode(this);
21516             }
21517             this.ownerTree = tree;
21518             var cs = this.childNodes;
21519             for(var i = 0, len = cs.length; i < len; i++) {
21520                 cs[i].setOwnerTree(tree);
21521             }
21522             if(tree){
21523                 tree.registerNode(this);
21524             }
21525         }
21526     },
21527
21528     /**
21529      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21530      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21531      * @return {String} The path
21532      */
21533     getPath : function(attr){
21534         attr = attr || "id";
21535         var p = this.parentNode;
21536         var b = [this.attributes[attr]];
21537         while(p){
21538             b.unshift(p.attributes[attr]);
21539             p = p.parentNode;
21540         }
21541         var sep = this.getOwnerTree().pathSeparator;
21542         return sep + b.join(sep);
21543     },
21544
21545     /**
21546      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21547      * function call will be the scope provided or the current node. The arguments to the function
21548      * will be the args provided or the current node. If the function returns false at any point,
21549      * the bubble is stopped.
21550      * @param {Function} fn The function to call
21551      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21552      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21553      */
21554     bubble : function(fn, scope, args){
21555         var p = this;
21556         while(p){
21557             if(fn.call(scope || p, args || p) === false){
21558                 break;
21559             }
21560             p = p.parentNode;
21561         }
21562     },
21563
21564     /**
21565      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21566      * function call will be the scope provided or the current node. The arguments to the function
21567      * will be the args provided or the current node. If the function returns false at any point,
21568      * the cascade is stopped on that branch.
21569      * @param {Function} fn The function to call
21570      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21571      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21572      */
21573     cascade : function(fn, scope, args){
21574         if(fn.call(scope || this, args || this) !== false){
21575             var cs = this.childNodes;
21576             for(var i = 0, len = cs.length; i < len; i++) {
21577                 cs[i].cascade(fn, scope, args);
21578             }
21579         }
21580     },
21581
21582     /**
21583      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21584      * function call will be the scope provided or the current node. The arguments to the function
21585      * will be the args provided or the current node. If the function returns false at any point,
21586      * the iteration stops.
21587      * @param {Function} fn The function to call
21588      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21589      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21590      */
21591     eachChild : function(fn, scope, args){
21592         var cs = this.childNodes;
21593         for(var i = 0, len = cs.length; i < len; i++) {
21594                 if(fn.call(scope || this, args || cs[i]) === false){
21595                     break;
21596                 }
21597         }
21598     },
21599
21600     /**
21601      * Finds the first child that has the attribute with the specified value.
21602      * @param {String} attribute The attribute name
21603      * @param {Mixed} value The value to search for
21604      * @return {Node} The found child or null if none was found
21605      */
21606     findChild : function(attribute, value){
21607         var cs = this.childNodes;
21608         for(var i = 0, len = cs.length; i < len; i++) {
21609                 if(cs[i].attributes[attribute] == value){
21610                     return cs[i];
21611                 }
21612         }
21613         return null;
21614     },
21615
21616     /**
21617      * Finds the first child by a custom function. The child matches if the function passed
21618      * returns true.
21619      * @param {Function} fn
21620      * @param {Object} scope (optional)
21621      * @return {Node} The found child or null if none was found
21622      */
21623     findChildBy : function(fn, scope){
21624         var cs = this.childNodes;
21625         for(var i = 0, len = cs.length; i < len; i++) {
21626                 if(fn.call(scope||cs[i], cs[i]) === true){
21627                     return cs[i];
21628                 }
21629         }
21630         return null;
21631     },
21632
21633     /**
21634      * Sorts this nodes children using the supplied sort function
21635      * @param {Function} fn
21636      * @param {Object} scope (optional)
21637      */
21638     sort : function(fn, scope){
21639         var cs = this.childNodes;
21640         var len = cs.length;
21641         if(len > 0){
21642             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21643             cs.sort(sortFn);
21644             for(var i = 0; i < len; i++){
21645                 var n = cs[i];
21646                 n.previousSibling = cs[i-1];
21647                 n.nextSibling = cs[i+1];
21648                 if(i == 0){
21649                     this.setFirstChild(n);
21650                 }
21651                 if(i == len-1){
21652                     this.setLastChild(n);
21653                 }
21654             }
21655         }
21656     },
21657
21658     /**
21659      * Returns true if this node is an ancestor (at any point) of the passed node.
21660      * @param {Node} node
21661      * @return {Boolean}
21662      */
21663     contains : function(node){
21664         return node.isAncestor(this);
21665     },
21666
21667     /**
21668      * Returns true if the passed node is an ancestor (at any point) of this node.
21669      * @param {Node} node
21670      * @return {Boolean}
21671      */
21672     isAncestor : function(node){
21673         var p = this.parentNode;
21674         while(p){
21675             if(p == node){
21676                 return true;
21677             }
21678             p = p.parentNode;
21679         }
21680         return false;
21681     },
21682
21683     toString : function(){
21684         return "[Node"+(this.id?" "+this.id:"")+"]";
21685     }
21686 });/*
21687  * Based on:
21688  * Ext JS Library 1.1.1
21689  * Copyright(c) 2006-2007, Ext JS, LLC.
21690  *
21691  * Originally Released Under LGPL - original licence link has changed is not relivant.
21692  *
21693  * Fork - LGPL
21694  * <script type="text/javascript">
21695  */
21696  
21697
21698 /**
21699  * @class Roo.ComponentMgr
21700  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21701  * @singleton
21702  */
21703 Roo.ComponentMgr = function(){
21704     var all = new Roo.util.MixedCollection();
21705
21706     return {
21707         /**
21708          * Registers a component.
21709          * @param {Roo.Component} c The component
21710          */
21711         register : function(c){
21712             all.add(c);
21713         },
21714
21715         /**
21716          * Unregisters a component.
21717          * @param {Roo.Component} c The component
21718          */
21719         unregister : function(c){
21720             all.remove(c);
21721         },
21722
21723         /**
21724          * Returns a component by id
21725          * @param {String} id The component id
21726          */
21727         get : function(id){
21728             return all.get(id);
21729         },
21730
21731         /**
21732          * Registers a function that will be called when a specified component is added to ComponentMgr
21733          * @param {String} id The component id
21734          * @param {Funtction} fn The callback function
21735          * @param {Object} scope The scope of the callback
21736          */
21737         onAvailable : function(id, fn, scope){
21738             all.on("add", function(index, o){
21739                 if(o.id == id){
21740                     fn.call(scope || o, o);
21741                     all.un("add", fn, scope);
21742                 }
21743             });
21744         }
21745     };
21746 }();/*
21747  * Based on:
21748  * Ext JS Library 1.1.1
21749  * Copyright(c) 2006-2007, Ext JS, LLC.
21750  *
21751  * Originally Released Under LGPL - original licence link has changed is not relivant.
21752  *
21753  * Fork - LGPL
21754  * <script type="text/javascript">
21755  */
21756  
21757 /**
21758  * @class Roo.Component
21759  * @extends Roo.util.Observable
21760  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21761  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21762  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21763  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21764  * All visual components (widgets) that require rendering into a layout should subclass Component.
21765  * @constructor
21766  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21767  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21768  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21769  */
21770 Roo.Component = function(config){
21771     config = config || {};
21772     if(config.tagName || config.dom || typeof config == "string"){ // element object
21773         config = {el: config, id: config.id || config};
21774     }
21775     this.initialConfig = config;
21776
21777     Roo.apply(this, config);
21778     this.addEvents({
21779         /**
21780          * @event disable
21781          * Fires after the component is disabled.
21782              * @param {Roo.Component} this
21783              */
21784         disable : true,
21785         /**
21786          * @event enable
21787          * Fires after the component is enabled.
21788              * @param {Roo.Component} this
21789              */
21790         enable : true,
21791         /**
21792          * @event beforeshow
21793          * Fires before the component is shown.  Return false to stop the show.
21794              * @param {Roo.Component} this
21795              */
21796         beforeshow : true,
21797         /**
21798          * @event show
21799          * Fires after the component is shown.
21800              * @param {Roo.Component} this
21801              */
21802         show : true,
21803         /**
21804          * @event beforehide
21805          * Fires before the component is hidden. Return false to stop the hide.
21806              * @param {Roo.Component} this
21807              */
21808         beforehide : true,
21809         /**
21810          * @event hide
21811          * Fires after the component is hidden.
21812              * @param {Roo.Component} this
21813              */
21814         hide : true,
21815         /**
21816          * @event beforerender
21817          * Fires before the component is rendered. Return false to stop the render.
21818              * @param {Roo.Component} this
21819              */
21820         beforerender : true,
21821         /**
21822          * @event render
21823          * Fires after the component is rendered.
21824              * @param {Roo.Component} this
21825              */
21826         render : true,
21827         /**
21828          * @event beforedestroy
21829          * Fires before the component is destroyed. Return false to stop the destroy.
21830              * @param {Roo.Component} this
21831              */
21832         beforedestroy : true,
21833         /**
21834          * @event destroy
21835          * Fires after the component is destroyed.
21836              * @param {Roo.Component} this
21837              */
21838         destroy : true
21839     });
21840     if(!this.id){
21841         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21842     }
21843     Roo.ComponentMgr.register(this);
21844     Roo.Component.superclass.constructor.call(this);
21845     this.initComponent();
21846     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21847         this.render(this.renderTo);
21848         delete this.renderTo;
21849     }
21850 };
21851
21852 // private
21853 Roo.Component.AUTO_ID = 1000;
21854
21855 Roo.extend(Roo.Component, Roo.util.Observable, {
21856     /**
21857      * @property {Boolean} hidden
21858      * true if this component is hidden. Read-only.
21859      */
21860     hidden : false,
21861     /**
21862      * true if this component is disabled. Read-only.
21863      */
21864     disabled : false,
21865     /**
21866      * true if this component has been rendered. Read-only.
21867      */
21868     rendered : false,
21869     
21870     /** @cfg {String} disableClass
21871      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21872      */
21873     disabledClass : "x-item-disabled",
21874         /** @cfg {Boolean} allowDomMove
21875          * Whether the component can move the Dom node when rendering (defaults to true).
21876          */
21877     allowDomMove : true,
21878     /** @cfg {String} hideMode
21879      * How this component should hidden. Supported values are
21880      * "visibility" (css visibility), "offsets" (negative offset position) and
21881      * "display" (css display) - defaults to "display".
21882      */
21883     hideMode: 'display',
21884
21885     // private
21886     ctype : "Roo.Component",
21887
21888     /** @cfg {String} actionMode 
21889      * which property holds the element that used for  hide() / show() / disable() / enable()
21890      * default is 'el' 
21891      */
21892     actionMode : "el",
21893
21894     // private
21895     getActionEl : function(){
21896         return this[this.actionMode];
21897     },
21898
21899     initComponent : Roo.emptyFn,
21900     /**
21901      * If this is a lazy rendering component, render it to its container element.
21902      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21903      */
21904     render : function(container, position){
21905         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21906             if(!container && this.el){
21907                 this.el = Roo.get(this.el);
21908                 container = this.el.dom.parentNode;
21909                 this.allowDomMove = false;
21910             }
21911             this.container = Roo.get(container);
21912             this.rendered = true;
21913             if(position !== undefined){
21914                 if(typeof position == 'number'){
21915                     position = this.container.dom.childNodes[position];
21916                 }else{
21917                     position = Roo.getDom(position);
21918                 }
21919             }
21920             this.onRender(this.container, position || null);
21921             if(this.cls){
21922                 this.el.addClass(this.cls);
21923                 delete this.cls;
21924             }
21925             if(this.style){
21926                 this.el.applyStyles(this.style);
21927                 delete this.style;
21928             }
21929             this.fireEvent("render", this);
21930             this.afterRender(this.container);
21931             if(this.hidden){
21932                 this.hide();
21933             }
21934             if(this.disabled){
21935                 this.disable();
21936             }
21937         }
21938         return this;
21939     },
21940
21941     // private
21942     // default function is not really useful
21943     onRender : function(ct, position){
21944         if(this.el){
21945             this.el = Roo.get(this.el);
21946             if(this.allowDomMove !== false){
21947                 ct.dom.insertBefore(this.el.dom, position);
21948             }
21949         }
21950     },
21951
21952     // private
21953     getAutoCreate : function(){
21954         var cfg = typeof this.autoCreate == "object" ?
21955                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21956         if(this.id && !cfg.id){
21957             cfg.id = this.id;
21958         }
21959         return cfg;
21960     },
21961
21962     // private
21963     afterRender : Roo.emptyFn,
21964
21965     /**
21966      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21967      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21968      */
21969     destroy : function(){
21970         if(this.fireEvent("beforedestroy", this) !== false){
21971             this.purgeListeners();
21972             this.beforeDestroy();
21973             if(this.rendered){
21974                 this.el.removeAllListeners();
21975                 this.el.remove();
21976                 if(this.actionMode == "container"){
21977                     this.container.remove();
21978                 }
21979             }
21980             this.onDestroy();
21981             Roo.ComponentMgr.unregister(this);
21982             this.fireEvent("destroy", this);
21983         }
21984     },
21985
21986         // private
21987     beforeDestroy : function(){
21988
21989     },
21990
21991         // private
21992         onDestroy : function(){
21993
21994     },
21995
21996     /**
21997      * Returns the underlying {@link Roo.Element}.
21998      * @return {Roo.Element} The element
21999      */
22000     getEl : function(){
22001         return this.el;
22002     },
22003
22004     /**
22005      * Returns the id of this component.
22006      * @return {String}
22007      */
22008     getId : function(){
22009         return this.id;
22010     },
22011
22012     /**
22013      * Try to focus this component.
22014      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22015      * @return {Roo.Component} this
22016      */
22017     focus : function(selectText){
22018         if(this.rendered){
22019             this.el.focus();
22020             if(selectText === true){
22021                 this.el.dom.select();
22022             }
22023         }
22024         return this;
22025     },
22026
22027     // private
22028     blur : function(){
22029         if(this.rendered){
22030             this.el.blur();
22031         }
22032         return this;
22033     },
22034
22035     /**
22036      * Disable this component.
22037      * @return {Roo.Component} this
22038      */
22039     disable : function(){
22040         if(this.rendered){
22041             this.onDisable();
22042         }
22043         this.disabled = true;
22044         this.fireEvent("disable", this);
22045         return this;
22046     },
22047
22048         // private
22049     onDisable : function(){
22050         this.getActionEl().addClass(this.disabledClass);
22051         this.el.dom.disabled = true;
22052     },
22053
22054     /**
22055      * Enable this component.
22056      * @return {Roo.Component} this
22057      */
22058     enable : function(){
22059         if(this.rendered){
22060             this.onEnable();
22061         }
22062         this.disabled = false;
22063         this.fireEvent("enable", this);
22064         return this;
22065     },
22066
22067         // private
22068     onEnable : function(){
22069         this.getActionEl().removeClass(this.disabledClass);
22070         this.el.dom.disabled = false;
22071     },
22072
22073     /**
22074      * Convenience function for setting disabled/enabled by boolean.
22075      * @param {Boolean} disabled
22076      */
22077     setDisabled : function(disabled){
22078         this[disabled ? "disable" : "enable"]();
22079     },
22080
22081     /**
22082      * Show this component.
22083      * @return {Roo.Component} this
22084      */
22085     show: function(){
22086         if(this.fireEvent("beforeshow", this) !== false){
22087             this.hidden = false;
22088             if(this.rendered){
22089                 this.onShow();
22090             }
22091             this.fireEvent("show", this);
22092         }
22093         return this;
22094     },
22095
22096     // private
22097     onShow : function(){
22098         var ae = this.getActionEl();
22099         if(this.hideMode == 'visibility'){
22100             ae.dom.style.visibility = "visible";
22101         }else if(this.hideMode == 'offsets'){
22102             ae.removeClass('x-hidden');
22103         }else{
22104             ae.dom.style.display = "";
22105         }
22106     },
22107
22108     /**
22109      * Hide this component.
22110      * @return {Roo.Component} this
22111      */
22112     hide: function(){
22113         if(this.fireEvent("beforehide", this) !== false){
22114             this.hidden = true;
22115             if(this.rendered){
22116                 this.onHide();
22117             }
22118             this.fireEvent("hide", this);
22119         }
22120         return this;
22121     },
22122
22123     // private
22124     onHide : function(){
22125         var ae = this.getActionEl();
22126         if(this.hideMode == 'visibility'){
22127             ae.dom.style.visibility = "hidden";
22128         }else if(this.hideMode == 'offsets'){
22129             ae.addClass('x-hidden');
22130         }else{
22131             ae.dom.style.display = "none";
22132         }
22133     },
22134
22135     /**
22136      * Convenience function to hide or show this component by boolean.
22137      * @param {Boolean} visible True to show, false to hide
22138      * @return {Roo.Component} this
22139      */
22140     setVisible: function(visible){
22141         if(visible) {
22142             this.show();
22143         }else{
22144             this.hide();
22145         }
22146         return this;
22147     },
22148
22149     /**
22150      * Returns true if this component is visible.
22151      */
22152     isVisible : function(){
22153         return this.getActionEl().isVisible();
22154     },
22155
22156     cloneConfig : function(overrides){
22157         overrides = overrides || {};
22158         var id = overrides.id || Roo.id();
22159         var cfg = Roo.applyIf(overrides, this.initialConfig);
22160         cfg.id = id; // prevent dup id
22161         return new this.constructor(cfg);
22162     }
22163 });/*
22164  * Based on:
22165  * Ext JS Library 1.1.1
22166  * Copyright(c) 2006-2007, Ext JS, LLC.
22167  *
22168  * Originally Released Under LGPL - original licence link has changed is not relivant.
22169  *
22170  * Fork - LGPL
22171  * <script type="text/javascript">
22172  */
22173  (function(){ 
22174 /**
22175  * @class Roo.Layer
22176  * @extends Roo.Element
22177  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22178  * automatic maintaining of shadow/shim positions.
22179  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22180  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22181  * you can pass a string with a CSS class name. False turns off the shadow.
22182  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22183  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22184  * @cfg {String} cls CSS class to add to the element
22185  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22186  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22187  * @constructor
22188  * @param {Object} config An object with config options.
22189  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22190  */
22191
22192 Roo.Layer = function(config, existingEl){
22193     config = config || {};
22194     var dh = Roo.DomHelper;
22195     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22196     if(existingEl){
22197         this.dom = Roo.getDom(existingEl);
22198     }
22199     if(!this.dom){
22200         var o = config.dh || {tag: "div", cls: "x-layer"};
22201         this.dom = dh.append(pel, o);
22202     }
22203     if(config.cls){
22204         this.addClass(config.cls);
22205     }
22206     this.constrain = config.constrain !== false;
22207     this.visibilityMode = Roo.Element.VISIBILITY;
22208     if(config.id){
22209         this.id = this.dom.id = config.id;
22210     }else{
22211         this.id = Roo.id(this.dom);
22212     }
22213     this.zindex = config.zindex || this.getZIndex();
22214     this.position("absolute", this.zindex);
22215     if(config.shadow){
22216         this.shadowOffset = config.shadowOffset || 4;
22217         this.shadow = new Roo.Shadow({
22218             offset : this.shadowOffset,
22219             mode : config.shadow
22220         });
22221     }else{
22222         this.shadowOffset = 0;
22223     }
22224     this.useShim = config.shim !== false && Roo.useShims;
22225     this.useDisplay = config.useDisplay;
22226     this.hide();
22227 };
22228
22229 var supr = Roo.Element.prototype;
22230
22231 // shims are shared among layer to keep from having 100 iframes
22232 var shims = [];
22233
22234 Roo.extend(Roo.Layer, Roo.Element, {
22235
22236     getZIndex : function(){
22237         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22238     },
22239
22240     getShim : function(){
22241         if(!this.useShim){
22242             return null;
22243         }
22244         if(this.shim){
22245             return this.shim;
22246         }
22247         var shim = shims.shift();
22248         if(!shim){
22249             shim = this.createShim();
22250             shim.enableDisplayMode('block');
22251             shim.dom.style.display = 'none';
22252             shim.dom.style.visibility = 'visible';
22253         }
22254         var pn = this.dom.parentNode;
22255         if(shim.dom.parentNode != pn){
22256             pn.insertBefore(shim.dom, this.dom);
22257         }
22258         shim.setStyle('z-index', this.getZIndex()-2);
22259         this.shim = shim;
22260         return shim;
22261     },
22262
22263     hideShim : function(){
22264         if(this.shim){
22265             this.shim.setDisplayed(false);
22266             shims.push(this.shim);
22267             delete this.shim;
22268         }
22269     },
22270
22271     disableShadow : function(){
22272         if(this.shadow){
22273             this.shadowDisabled = true;
22274             this.shadow.hide();
22275             this.lastShadowOffset = this.shadowOffset;
22276             this.shadowOffset = 0;
22277         }
22278     },
22279
22280     enableShadow : function(show){
22281         if(this.shadow){
22282             this.shadowDisabled = false;
22283             this.shadowOffset = this.lastShadowOffset;
22284             delete this.lastShadowOffset;
22285             if(show){
22286                 this.sync(true);
22287             }
22288         }
22289     },
22290
22291     // private
22292     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22293     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22294     sync : function(doShow){
22295         var sw = this.shadow;
22296         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22297             var sh = this.getShim();
22298
22299             var w = this.getWidth(),
22300                 h = this.getHeight();
22301
22302             var l = this.getLeft(true),
22303                 t = this.getTop(true);
22304
22305             if(sw && !this.shadowDisabled){
22306                 if(doShow && !sw.isVisible()){
22307                     sw.show(this);
22308                 }else{
22309                     sw.realign(l, t, w, h);
22310                 }
22311                 if(sh){
22312                     if(doShow){
22313                        sh.show();
22314                     }
22315                     // fit the shim behind the shadow, so it is shimmed too
22316                     var a = sw.adjusts, s = sh.dom.style;
22317                     s.left = (Math.min(l, l+a.l))+"px";
22318                     s.top = (Math.min(t, t+a.t))+"px";
22319                     s.width = (w+a.w)+"px";
22320                     s.height = (h+a.h)+"px";
22321                 }
22322             }else if(sh){
22323                 if(doShow){
22324                    sh.show();
22325                 }
22326                 sh.setSize(w, h);
22327                 sh.setLeftTop(l, t);
22328             }
22329             
22330         }
22331     },
22332
22333     // private
22334     destroy : function(){
22335         this.hideShim();
22336         if(this.shadow){
22337             this.shadow.hide();
22338         }
22339         this.removeAllListeners();
22340         var pn = this.dom.parentNode;
22341         if(pn){
22342             pn.removeChild(this.dom);
22343         }
22344         Roo.Element.uncache(this.id);
22345     },
22346
22347     remove : function(){
22348         this.destroy();
22349     },
22350
22351     // private
22352     beginUpdate : function(){
22353         this.updating = true;
22354     },
22355
22356     // private
22357     endUpdate : function(){
22358         this.updating = false;
22359         this.sync(true);
22360     },
22361
22362     // private
22363     hideUnders : function(negOffset){
22364         if(this.shadow){
22365             this.shadow.hide();
22366         }
22367         this.hideShim();
22368     },
22369
22370     // private
22371     constrainXY : function(){
22372         if(this.constrain){
22373             var vw = Roo.lib.Dom.getViewWidth(),
22374                 vh = Roo.lib.Dom.getViewHeight();
22375             var s = Roo.get(document).getScroll();
22376
22377             var xy = this.getXY();
22378             var x = xy[0], y = xy[1];   
22379             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22380             // only move it if it needs it
22381             var moved = false;
22382             // first validate right/bottom
22383             if((x + w) > vw+s.left){
22384                 x = vw - w - this.shadowOffset;
22385                 moved = true;
22386             }
22387             if((y + h) > vh+s.top){
22388                 y = vh - h - this.shadowOffset;
22389                 moved = true;
22390             }
22391             // then make sure top/left isn't negative
22392             if(x < s.left){
22393                 x = s.left;
22394                 moved = true;
22395             }
22396             if(y < s.top){
22397                 y = s.top;
22398                 moved = true;
22399             }
22400             if(moved){
22401                 if(this.avoidY){
22402                     var ay = this.avoidY;
22403                     if(y <= ay && (y+h) >= ay){
22404                         y = ay-h-5;   
22405                     }
22406                 }
22407                 xy = [x, y];
22408                 this.storeXY(xy);
22409                 supr.setXY.call(this, xy);
22410                 this.sync();
22411             }
22412         }
22413     },
22414
22415     isVisible : function(){
22416         return this.visible;    
22417     },
22418
22419     // private
22420     showAction : function(){
22421         this.visible = true; // track visibility to prevent getStyle calls
22422         if(this.useDisplay === true){
22423             this.setDisplayed("");
22424         }else if(this.lastXY){
22425             supr.setXY.call(this, this.lastXY);
22426         }else if(this.lastLT){
22427             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22428         }
22429     },
22430
22431     // private
22432     hideAction : function(){
22433         this.visible = false;
22434         if(this.useDisplay === true){
22435             this.setDisplayed(false);
22436         }else{
22437             this.setLeftTop(-10000,-10000);
22438         }
22439     },
22440
22441     // overridden Element method
22442     setVisible : function(v, a, d, c, e){
22443         if(v){
22444             this.showAction();
22445         }
22446         if(a && v){
22447             var cb = function(){
22448                 this.sync(true);
22449                 if(c){
22450                     c();
22451                 }
22452             }.createDelegate(this);
22453             supr.setVisible.call(this, true, true, d, cb, e);
22454         }else{
22455             if(!v){
22456                 this.hideUnders(true);
22457             }
22458             var cb = c;
22459             if(a){
22460                 cb = function(){
22461                     this.hideAction();
22462                     if(c){
22463                         c();
22464                     }
22465                 }.createDelegate(this);
22466             }
22467             supr.setVisible.call(this, v, a, d, cb, e);
22468             if(v){
22469                 this.sync(true);
22470             }else if(!a){
22471                 this.hideAction();
22472             }
22473         }
22474     },
22475
22476     storeXY : function(xy){
22477         delete this.lastLT;
22478         this.lastXY = xy;
22479     },
22480
22481     storeLeftTop : function(left, top){
22482         delete this.lastXY;
22483         this.lastLT = [left, top];
22484     },
22485
22486     // private
22487     beforeFx : function(){
22488         this.beforeAction();
22489         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22490     },
22491
22492     // private
22493     afterFx : function(){
22494         Roo.Layer.superclass.afterFx.apply(this, arguments);
22495         this.sync(this.isVisible());
22496     },
22497
22498     // private
22499     beforeAction : function(){
22500         if(!this.updating && this.shadow){
22501             this.shadow.hide();
22502         }
22503     },
22504
22505     // overridden Element method
22506     setLeft : function(left){
22507         this.storeLeftTop(left, this.getTop(true));
22508         supr.setLeft.apply(this, arguments);
22509         this.sync();
22510     },
22511
22512     setTop : function(top){
22513         this.storeLeftTop(this.getLeft(true), top);
22514         supr.setTop.apply(this, arguments);
22515         this.sync();
22516     },
22517
22518     setLeftTop : function(left, top){
22519         this.storeLeftTop(left, top);
22520         supr.setLeftTop.apply(this, arguments);
22521         this.sync();
22522     },
22523
22524     setXY : function(xy, a, d, c, e){
22525         this.fixDisplay();
22526         this.beforeAction();
22527         this.storeXY(xy);
22528         var cb = this.createCB(c);
22529         supr.setXY.call(this, xy, a, d, cb, e);
22530         if(!a){
22531             cb();
22532         }
22533     },
22534
22535     // private
22536     createCB : function(c){
22537         var el = this;
22538         return function(){
22539             el.constrainXY();
22540             el.sync(true);
22541             if(c){
22542                 c();
22543             }
22544         };
22545     },
22546
22547     // overridden Element method
22548     setX : function(x, a, d, c, e){
22549         this.setXY([x, this.getY()], a, d, c, e);
22550     },
22551
22552     // overridden Element method
22553     setY : function(y, a, d, c, e){
22554         this.setXY([this.getX(), y], a, d, c, e);
22555     },
22556
22557     // overridden Element method
22558     setSize : function(w, h, a, d, c, e){
22559         this.beforeAction();
22560         var cb = this.createCB(c);
22561         supr.setSize.call(this, w, h, a, d, cb, e);
22562         if(!a){
22563             cb();
22564         }
22565     },
22566
22567     // overridden Element method
22568     setWidth : function(w, a, d, c, e){
22569         this.beforeAction();
22570         var cb = this.createCB(c);
22571         supr.setWidth.call(this, w, a, d, cb, e);
22572         if(!a){
22573             cb();
22574         }
22575     },
22576
22577     // overridden Element method
22578     setHeight : function(h, a, d, c, e){
22579         this.beforeAction();
22580         var cb = this.createCB(c);
22581         supr.setHeight.call(this, h, a, d, cb, e);
22582         if(!a){
22583             cb();
22584         }
22585     },
22586
22587     // overridden Element method
22588     setBounds : function(x, y, w, h, a, d, c, e){
22589         this.beforeAction();
22590         var cb = this.createCB(c);
22591         if(!a){
22592             this.storeXY([x, y]);
22593             supr.setXY.call(this, [x, y]);
22594             supr.setSize.call(this, w, h, a, d, cb, e);
22595             cb();
22596         }else{
22597             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22598         }
22599         return this;
22600     },
22601     
22602     /**
22603      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22604      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22605      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22606      * @param {Number} zindex The new z-index to set
22607      * @return {this} The Layer
22608      */
22609     setZIndex : function(zindex){
22610         this.zindex = zindex;
22611         this.setStyle("z-index", zindex + 2);
22612         if(this.shadow){
22613             this.shadow.setZIndex(zindex + 1);
22614         }
22615         if(this.shim){
22616             this.shim.setStyle("z-index", zindex);
22617         }
22618     }
22619 });
22620 })();/*
22621  * Based on:
22622  * Ext JS Library 1.1.1
22623  * Copyright(c) 2006-2007, Ext JS, LLC.
22624  *
22625  * Originally Released Under LGPL - original licence link has changed is not relivant.
22626  *
22627  * Fork - LGPL
22628  * <script type="text/javascript">
22629  */
22630
22631
22632 /**
22633  * @class Roo.Shadow
22634  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22635  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22636  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22637  * @constructor
22638  * Create a new Shadow
22639  * @param {Object} config The config object
22640  */
22641 Roo.Shadow = function(config){
22642     Roo.apply(this, config);
22643     if(typeof this.mode != "string"){
22644         this.mode = this.defaultMode;
22645     }
22646     var o = this.offset, a = {h: 0};
22647     var rad = Math.floor(this.offset/2);
22648     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22649         case "drop":
22650             a.w = 0;
22651             a.l = a.t = o;
22652             a.t -= 1;
22653             if(Roo.isIE){
22654                 a.l -= this.offset + rad;
22655                 a.t -= this.offset + rad;
22656                 a.w -= rad;
22657                 a.h -= rad;
22658                 a.t += 1;
22659             }
22660         break;
22661         case "sides":
22662             a.w = (o*2);
22663             a.l = -o;
22664             a.t = o-1;
22665             if(Roo.isIE){
22666                 a.l -= (this.offset - rad);
22667                 a.t -= this.offset + rad;
22668                 a.l += 1;
22669                 a.w -= (this.offset - rad)*2;
22670                 a.w -= rad + 1;
22671                 a.h -= 1;
22672             }
22673         break;
22674         case "frame":
22675             a.w = a.h = (o*2);
22676             a.l = a.t = -o;
22677             a.t += 1;
22678             a.h -= 2;
22679             if(Roo.isIE){
22680                 a.l -= (this.offset - rad);
22681                 a.t -= (this.offset - rad);
22682                 a.l += 1;
22683                 a.w -= (this.offset + rad + 1);
22684                 a.h -= (this.offset + rad);
22685                 a.h += 1;
22686             }
22687         break;
22688     };
22689
22690     this.adjusts = a;
22691 };
22692
22693 Roo.Shadow.prototype = {
22694     /**
22695      * @cfg {String} mode
22696      * The shadow display mode.  Supports the following options:<br />
22697      * sides: Shadow displays on both sides and bottom only<br />
22698      * frame: Shadow displays equally on all four sides<br />
22699      * drop: Traditional bottom-right drop shadow (default)
22700      */
22701     /**
22702      * @cfg {String} offset
22703      * The number of pixels to offset the shadow from the element (defaults to 4)
22704      */
22705     offset: 4,
22706
22707     // private
22708     defaultMode: "drop",
22709
22710     /**
22711      * Displays the shadow under the target element
22712      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22713      */
22714     show : function(target){
22715         target = Roo.get(target);
22716         if(!this.el){
22717             this.el = Roo.Shadow.Pool.pull();
22718             if(this.el.dom.nextSibling != target.dom){
22719                 this.el.insertBefore(target);
22720             }
22721         }
22722         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22723         if(Roo.isIE){
22724             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22725         }
22726         this.realign(
22727             target.getLeft(true),
22728             target.getTop(true),
22729             target.getWidth(),
22730             target.getHeight()
22731         );
22732         this.el.dom.style.display = "block";
22733     },
22734
22735     /**
22736      * Returns true if the shadow is visible, else false
22737      */
22738     isVisible : function(){
22739         return this.el ? true : false;  
22740     },
22741
22742     /**
22743      * Direct alignment when values are already available. Show must be called at least once before
22744      * calling this method to ensure it is initialized.
22745      * @param {Number} left The target element left position
22746      * @param {Number} top The target element top position
22747      * @param {Number} width The target element width
22748      * @param {Number} height The target element height
22749      */
22750     realign : function(l, t, w, h){
22751         if(!this.el){
22752             return;
22753         }
22754         var a = this.adjusts, d = this.el.dom, s = d.style;
22755         var iea = 0;
22756         s.left = (l+a.l)+"px";
22757         s.top = (t+a.t)+"px";
22758         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22759  
22760         if(s.width != sws || s.height != shs){
22761             s.width = sws;
22762             s.height = shs;
22763             if(!Roo.isIE){
22764                 var cn = d.childNodes;
22765                 var sww = Math.max(0, (sw-12))+"px";
22766                 cn[0].childNodes[1].style.width = sww;
22767                 cn[1].childNodes[1].style.width = sww;
22768                 cn[2].childNodes[1].style.width = sww;
22769                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22770             }
22771         }
22772     },
22773
22774     /**
22775      * Hides this shadow
22776      */
22777     hide : function(){
22778         if(this.el){
22779             this.el.dom.style.display = "none";
22780             Roo.Shadow.Pool.push(this.el);
22781             delete this.el;
22782         }
22783     },
22784
22785     /**
22786      * Adjust the z-index of this shadow
22787      * @param {Number} zindex The new z-index
22788      */
22789     setZIndex : function(z){
22790         this.zIndex = z;
22791         if(this.el){
22792             this.el.setStyle("z-index", z);
22793         }
22794     }
22795 };
22796
22797 // Private utility class that manages the internal Shadow cache
22798 Roo.Shadow.Pool = function(){
22799     var p = [];
22800     var markup = Roo.isIE ?
22801                  '<div class="x-ie-shadow"></div>' :
22802                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22803     return {
22804         pull : function(){
22805             var sh = p.shift();
22806             if(!sh){
22807                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22808                 sh.autoBoxAdjust = false;
22809             }
22810             return sh;
22811         },
22812
22813         push : function(sh){
22814             p.push(sh);
22815         }
22816     };
22817 }();/*
22818  * Based on:
22819  * Ext JS Library 1.1.1
22820  * Copyright(c) 2006-2007, Ext JS, LLC.
22821  *
22822  * Originally Released Under LGPL - original licence link has changed is not relivant.
22823  *
22824  * Fork - LGPL
22825  * <script type="text/javascript">
22826  */
22827
22828 /**
22829  * @class Roo.BoxComponent
22830  * @extends Roo.Component
22831  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22832  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22833  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22834  * layout containers.
22835  * @constructor
22836  * @param {Roo.Element/String/Object} config The configuration options.
22837  */
22838 Roo.BoxComponent = function(config){
22839     Roo.Component.call(this, config);
22840     this.addEvents({
22841         /**
22842          * @event resize
22843          * Fires after the component is resized.
22844              * @param {Roo.Component} this
22845              * @param {Number} adjWidth The box-adjusted width that was set
22846              * @param {Number} adjHeight The box-adjusted height that was set
22847              * @param {Number} rawWidth The width that was originally specified
22848              * @param {Number} rawHeight The height that was originally specified
22849              */
22850         resize : true,
22851         /**
22852          * @event move
22853          * Fires after the component is moved.
22854              * @param {Roo.Component} this
22855              * @param {Number} x The new x position
22856              * @param {Number} y The new y position
22857              */
22858         move : true
22859     });
22860 };
22861
22862 Roo.extend(Roo.BoxComponent, Roo.Component, {
22863     // private, set in afterRender to signify that the component has been rendered
22864     boxReady : false,
22865     // private, used to defer height settings to subclasses
22866     deferHeight: false,
22867     /** @cfg {Number} width
22868      * width (optional) size of component
22869      */
22870      /** @cfg {Number} height
22871      * height (optional) size of component
22872      */
22873      
22874     /**
22875      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22876      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22877      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22878      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22879      * @return {Roo.BoxComponent} this
22880      */
22881     setSize : function(w, h){
22882         // support for standard size objects
22883         if(typeof w == 'object'){
22884             h = w.height;
22885             w = w.width;
22886         }
22887         // not rendered
22888         if(!this.boxReady){
22889             this.width = w;
22890             this.height = h;
22891             return this;
22892         }
22893
22894         // prevent recalcs when not needed
22895         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22896             return this;
22897         }
22898         this.lastSize = {width: w, height: h};
22899
22900         var adj = this.adjustSize(w, h);
22901         var aw = adj.width, ah = adj.height;
22902         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22903             var rz = this.getResizeEl();
22904             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22905                 rz.setSize(aw, ah);
22906             }else if(!this.deferHeight && ah !== undefined){
22907                 rz.setHeight(ah);
22908             }else if(aw !== undefined){
22909                 rz.setWidth(aw);
22910             }
22911             this.onResize(aw, ah, w, h);
22912             this.fireEvent('resize', this, aw, ah, w, h);
22913         }
22914         return this;
22915     },
22916
22917     /**
22918      * Gets the current size of the component's underlying element.
22919      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22920      */
22921     getSize : function(){
22922         return this.el.getSize();
22923     },
22924
22925     /**
22926      * Gets the current XY position of the component's underlying element.
22927      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22928      * @return {Array} The XY position of the element (e.g., [100, 200])
22929      */
22930     getPosition : function(local){
22931         if(local === true){
22932             return [this.el.getLeft(true), this.el.getTop(true)];
22933         }
22934         return this.xy || this.el.getXY();
22935     },
22936
22937     /**
22938      * Gets the current box measurements of the component's underlying element.
22939      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22940      * @returns {Object} box An object in the format {x, y, width, height}
22941      */
22942     getBox : function(local){
22943         var s = this.el.getSize();
22944         if(local){
22945             s.x = this.el.getLeft(true);
22946             s.y = this.el.getTop(true);
22947         }else{
22948             var xy = this.xy || this.el.getXY();
22949             s.x = xy[0];
22950             s.y = xy[1];
22951         }
22952         return s;
22953     },
22954
22955     /**
22956      * Sets the current box measurements of the component's underlying element.
22957      * @param {Object} box An object in the format {x, y, width, height}
22958      * @returns {Roo.BoxComponent} this
22959      */
22960     updateBox : function(box){
22961         this.setSize(box.width, box.height);
22962         this.setPagePosition(box.x, box.y);
22963         return this;
22964     },
22965
22966     // protected
22967     getResizeEl : function(){
22968         return this.resizeEl || this.el;
22969     },
22970
22971     // protected
22972     getPositionEl : function(){
22973         return this.positionEl || this.el;
22974     },
22975
22976     /**
22977      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22978      * This method fires the move event.
22979      * @param {Number} left The new left
22980      * @param {Number} top The new top
22981      * @returns {Roo.BoxComponent} this
22982      */
22983     setPosition : function(x, y){
22984         this.x = x;
22985         this.y = y;
22986         if(!this.boxReady){
22987             return this;
22988         }
22989         var adj = this.adjustPosition(x, y);
22990         var ax = adj.x, ay = adj.y;
22991
22992         var el = this.getPositionEl();
22993         if(ax !== undefined || ay !== undefined){
22994             if(ax !== undefined && ay !== undefined){
22995                 el.setLeftTop(ax, ay);
22996             }else if(ax !== undefined){
22997                 el.setLeft(ax);
22998             }else if(ay !== undefined){
22999                 el.setTop(ay);
23000             }
23001             this.onPosition(ax, ay);
23002             this.fireEvent('move', this, ax, ay);
23003         }
23004         return this;
23005     },
23006
23007     /**
23008      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23009      * This method fires the move event.
23010      * @param {Number} x The new x position
23011      * @param {Number} y The new y position
23012      * @returns {Roo.BoxComponent} this
23013      */
23014     setPagePosition : function(x, y){
23015         this.pageX = x;
23016         this.pageY = y;
23017         if(!this.boxReady){
23018             return;
23019         }
23020         if(x === undefined || y === undefined){ // cannot translate undefined points
23021             return;
23022         }
23023         var p = this.el.translatePoints(x, y);
23024         this.setPosition(p.left, p.top);
23025         return this;
23026     },
23027
23028     // private
23029     onRender : function(ct, position){
23030         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23031         if(this.resizeEl){
23032             this.resizeEl = Roo.get(this.resizeEl);
23033         }
23034         if(this.positionEl){
23035             this.positionEl = Roo.get(this.positionEl);
23036         }
23037     },
23038
23039     // private
23040     afterRender : function(){
23041         Roo.BoxComponent.superclass.afterRender.call(this);
23042         this.boxReady = true;
23043         this.setSize(this.width, this.height);
23044         if(this.x || this.y){
23045             this.setPosition(this.x, this.y);
23046         }
23047         if(this.pageX || this.pageY){
23048             this.setPagePosition(this.pageX, this.pageY);
23049         }
23050     },
23051
23052     /**
23053      * Force the component's size to recalculate based on the underlying element's current height and width.
23054      * @returns {Roo.BoxComponent} this
23055      */
23056     syncSize : function(){
23057         delete this.lastSize;
23058         this.setSize(this.el.getWidth(), this.el.getHeight());
23059         return this;
23060     },
23061
23062     /**
23063      * Called after the component is resized, this method is empty by default but can be implemented by any
23064      * subclass that needs to perform custom logic after a resize occurs.
23065      * @param {Number} adjWidth The box-adjusted width that was set
23066      * @param {Number} adjHeight The box-adjusted height that was set
23067      * @param {Number} rawWidth The width that was originally specified
23068      * @param {Number} rawHeight The height that was originally specified
23069      */
23070     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23071
23072     },
23073
23074     /**
23075      * Called after the component is moved, this method is empty by default but can be implemented by any
23076      * subclass that needs to perform custom logic after a move occurs.
23077      * @param {Number} x The new x position
23078      * @param {Number} y The new y position
23079      */
23080     onPosition : function(x, y){
23081
23082     },
23083
23084     // private
23085     adjustSize : function(w, h){
23086         if(this.autoWidth){
23087             w = 'auto';
23088         }
23089         if(this.autoHeight){
23090             h = 'auto';
23091         }
23092         return {width : w, height: h};
23093     },
23094
23095     // private
23096     adjustPosition : function(x, y){
23097         return {x : x, y: y};
23098     }
23099 });/*
23100  * Based on:
23101  * Ext JS Library 1.1.1
23102  * Copyright(c) 2006-2007, Ext JS, LLC.
23103  *
23104  * Originally Released Under LGPL - original licence link has changed is not relivant.
23105  *
23106  * Fork - LGPL
23107  * <script type="text/javascript">
23108  */
23109
23110
23111 /**
23112  * @class Roo.SplitBar
23113  * @extends Roo.util.Observable
23114  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23115  * <br><br>
23116  * Usage:
23117  * <pre><code>
23118 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23119                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23120 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23121 split.minSize = 100;
23122 split.maxSize = 600;
23123 split.animate = true;
23124 split.on('moved', splitterMoved);
23125 </code></pre>
23126  * @constructor
23127  * Create a new SplitBar
23128  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23129  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23130  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23131  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23132                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23133                         position of the SplitBar).
23134  */
23135 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23136     
23137     /** @private */
23138     this.el = Roo.get(dragElement, true);
23139     this.el.dom.unselectable = "on";
23140     /** @private */
23141     this.resizingEl = Roo.get(resizingElement, true);
23142
23143     /**
23144      * @private
23145      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23146      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23147      * @type Number
23148      */
23149     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23150     
23151     /**
23152      * The minimum size of the resizing element. (Defaults to 0)
23153      * @type Number
23154      */
23155     this.minSize = 0;
23156     
23157     /**
23158      * The maximum size of the resizing element. (Defaults to 2000)
23159      * @type Number
23160      */
23161     this.maxSize = 2000;
23162     
23163     /**
23164      * Whether to animate the transition to the new size
23165      * @type Boolean
23166      */
23167     this.animate = false;
23168     
23169     /**
23170      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23171      * @type Boolean
23172      */
23173     this.useShim = false;
23174     
23175     /** @private */
23176     this.shim = null;
23177     
23178     if(!existingProxy){
23179         /** @private */
23180         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23181     }else{
23182         this.proxy = Roo.get(existingProxy).dom;
23183     }
23184     /** @private */
23185     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23186     
23187     /** @private */
23188     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23189     
23190     /** @private */
23191     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23192     
23193     /** @private */
23194     this.dragSpecs = {};
23195     
23196     /**
23197      * @private The adapter to use to positon and resize elements
23198      */
23199     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23200     this.adapter.init(this);
23201     
23202     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23203         /** @private */
23204         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23205         this.el.addClass("x-splitbar-h");
23206     }else{
23207         /** @private */
23208         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23209         this.el.addClass("x-splitbar-v");
23210     }
23211     
23212     this.addEvents({
23213         /**
23214          * @event resize
23215          * Fires when the splitter is moved (alias for {@link #event-moved})
23216          * @param {Roo.SplitBar} this
23217          * @param {Number} newSize the new width or height
23218          */
23219         "resize" : true,
23220         /**
23221          * @event moved
23222          * Fires when the splitter is moved
23223          * @param {Roo.SplitBar} this
23224          * @param {Number} newSize the new width or height
23225          */
23226         "moved" : true,
23227         /**
23228          * @event beforeresize
23229          * Fires before the splitter is dragged
23230          * @param {Roo.SplitBar} this
23231          */
23232         "beforeresize" : true,
23233
23234         "beforeapply" : true
23235     });
23236
23237     Roo.util.Observable.call(this);
23238 };
23239
23240 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23241     onStartProxyDrag : function(x, y){
23242         this.fireEvent("beforeresize", this);
23243         if(!this.overlay){
23244             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23245             o.unselectable();
23246             o.enableDisplayMode("block");
23247             // all splitbars share the same overlay
23248             Roo.SplitBar.prototype.overlay = o;
23249         }
23250         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23251         this.overlay.show();
23252         Roo.get(this.proxy).setDisplayed("block");
23253         var size = this.adapter.getElementSize(this);
23254         this.activeMinSize = this.getMinimumSize();;
23255         this.activeMaxSize = this.getMaximumSize();;
23256         var c1 = size - this.activeMinSize;
23257         var c2 = Math.max(this.activeMaxSize - size, 0);
23258         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23259             this.dd.resetConstraints();
23260             this.dd.setXConstraint(
23261                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23262                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23263             );
23264             this.dd.setYConstraint(0, 0);
23265         }else{
23266             this.dd.resetConstraints();
23267             this.dd.setXConstraint(0, 0);
23268             this.dd.setYConstraint(
23269                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23270                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23271             );
23272          }
23273         this.dragSpecs.startSize = size;
23274         this.dragSpecs.startPoint = [x, y];
23275         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23276     },
23277     
23278     /** 
23279      * @private Called after the drag operation by the DDProxy
23280      */
23281     onEndProxyDrag : function(e){
23282         Roo.get(this.proxy).setDisplayed(false);
23283         var endPoint = Roo.lib.Event.getXY(e);
23284         if(this.overlay){
23285             this.overlay.hide();
23286         }
23287         var newSize;
23288         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23289             newSize = this.dragSpecs.startSize + 
23290                 (this.placement == Roo.SplitBar.LEFT ?
23291                     endPoint[0] - this.dragSpecs.startPoint[0] :
23292                     this.dragSpecs.startPoint[0] - endPoint[0]
23293                 );
23294         }else{
23295             newSize = this.dragSpecs.startSize + 
23296                 (this.placement == Roo.SplitBar.TOP ?
23297                     endPoint[1] - this.dragSpecs.startPoint[1] :
23298                     this.dragSpecs.startPoint[1] - endPoint[1]
23299                 );
23300         }
23301         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23302         if(newSize != this.dragSpecs.startSize){
23303             if(this.fireEvent('beforeapply', this, newSize) !== false){
23304                 this.adapter.setElementSize(this, newSize);
23305                 this.fireEvent("moved", this, newSize);
23306                 this.fireEvent("resize", this, newSize);
23307             }
23308         }
23309     },
23310     
23311     /**
23312      * Get the adapter this SplitBar uses
23313      * @return The adapter object
23314      */
23315     getAdapter : function(){
23316         return this.adapter;
23317     },
23318     
23319     /**
23320      * Set the adapter this SplitBar uses
23321      * @param {Object} adapter A SplitBar adapter object
23322      */
23323     setAdapter : function(adapter){
23324         this.adapter = adapter;
23325         this.adapter.init(this);
23326     },
23327     
23328     /**
23329      * Gets the minimum size for the resizing element
23330      * @return {Number} The minimum size
23331      */
23332     getMinimumSize : function(){
23333         return this.minSize;
23334     },
23335     
23336     /**
23337      * Sets the minimum size for the resizing element
23338      * @param {Number} minSize The minimum size
23339      */
23340     setMinimumSize : function(minSize){
23341         this.minSize = minSize;
23342     },
23343     
23344     /**
23345      * Gets the maximum size for the resizing element
23346      * @return {Number} The maximum size
23347      */
23348     getMaximumSize : function(){
23349         return this.maxSize;
23350     },
23351     
23352     /**
23353      * Sets the maximum size for the resizing element
23354      * @param {Number} maxSize The maximum size
23355      */
23356     setMaximumSize : function(maxSize){
23357         this.maxSize = maxSize;
23358     },
23359     
23360     /**
23361      * Sets the initialize size for the resizing element
23362      * @param {Number} size The initial size
23363      */
23364     setCurrentSize : function(size){
23365         var oldAnimate = this.animate;
23366         this.animate = false;
23367         this.adapter.setElementSize(this, size);
23368         this.animate = oldAnimate;
23369     },
23370     
23371     /**
23372      * Destroy this splitbar. 
23373      * @param {Boolean} removeEl True to remove the element
23374      */
23375     destroy : function(removeEl){
23376         if(this.shim){
23377             this.shim.remove();
23378         }
23379         this.dd.unreg();
23380         this.proxy.parentNode.removeChild(this.proxy);
23381         if(removeEl){
23382             this.el.remove();
23383         }
23384     }
23385 });
23386
23387 /**
23388  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23389  */
23390 Roo.SplitBar.createProxy = function(dir){
23391     var proxy = new Roo.Element(document.createElement("div"));
23392     proxy.unselectable();
23393     var cls = 'x-splitbar-proxy';
23394     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23395     document.body.appendChild(proxy.dom);
23396     return proxy.dom;
23397 };
23398
23399 /** 
23400  * @class Roo.SplitBar.BasicLayoutAdapter
23401  * Default Adapter. It assumes the splitter and resizing element are not positioned
23402  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23403  */
23404 Roo.SplitBar.BasicLayoutAdapter = function(){
23405 };
23406
23407 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23408     // do nothing for now
23409     init : function(s){
23410     
23411     },
23412     /**
23413      * Called before drag operations to get the current size of the resizing element. 
23414      * @param {Roo.SplitBar} s The SplitBar using this adapter
23415      */
23416      getElementSize : function(s){
23417         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23418             return s.resizingEl.getWidth();
23419         }else{
23420             return s.resizingEl.getHeight();
23421         }
23422     },
23423     
23424     /**
23425      * Called after drag operations to set the size of the resizing element.
23426      * @param {Roo.SplitBar} s The SplitBar using this adapter
23427      * @param {Number} newSize The new size to set
23428      * @param {Function} onComplete A function to be invoked when resizing is complete
23429      */
23430     setElementSize : function(s, newSize, onComplete){
23431         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23432             if(!s.animate){
23433                 s.resizingEl.setWidth(newSize);
23434                 if(onComplete){
23435                     onComplete(s, newSize);
23436                 }
23437             }else{
23438                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23439             }
23440         }else{
23441             
23442             if(!s.animate){
23443                 s.resizingEl.setHeight(newSize);
23444                 if(onComplete){
23445                     onComplete(s, newSize);
23446                 }
23447             }else{
23448                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23449             }
23450         }
23451     }
23452 };
23453
23454 /** 
23455  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23456  * @extends Roo.SplitBar.BasicLayoutAdapter
23457  * Adapter that  moves the splitter element to align with the resized sizing element. 
23458  * Used with an absolute positioned SplitBar.
23459  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23460  * document.body, make sure you assign an id to the body element.
23461  */
23462 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23463     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23464     this.container = Roo.get(container);
23465 };
23466
23467 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23468     init : function(s){
23469         this.basic.init(s);
23470     },
23471     
23472     getElementSize : function(s){
23473         return this.basic.getElementSize(s);
23474     },
23475     
23476     setElementSize : function(s, newSize, onComplete){
23477         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23478     },
23479     
23480     moveSplitter : function(s){
23481         var yes = Roo.SplitBar;
23482         switch(s.placement){
23483             case yes.LEFT:
23484                 s.el.setX(s.resizingEl.getRight());
23485                 break;
23486             case yes.RIGHT:
23487                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23488                 break;
23489             case yes.TOP:
23490                 s.el.setY(s.resizingEl.getBottom());
23491                 break;
23492             case yes.BOTTOM:
23493                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23494                 break;
23495         }
23496     }
23497 };
23498
23499 /**
23500  * Orientation constant - Create a vertical SplitBar
23501  * @static
23502  * @type Number
23503  */
23504 Roo.SplitBar.VERTICAL = 1;
23505
23506 /**
23507  * Orientation constant - Create a horizontal SplitBar
23508  * @static
23509  * @type Number
23510  */
23511 Roo.SplitBar.HORIZONTAL = 2;
23512
23513 /**
23514  * Placement constant - The resizing element is to the left of the splitter element
23515  * @static
23516  * @type Number
23517  */
23518 Roo.SplitBar.LEFT = 1;
23519
23520 /**
23521  * Placement constant - The resizing element is to the right of the splitter element
23522  * @static
23523  * @type Number
23524  */
23525 Roo.SplitBar.RIGHT = 2;
23526
23527 /**
23528  * Placement constant - The resizing element is positioned above the splitter element
23529  * @static
23530  * @type Number
23531  */
23532 Roo.SplitBar.TOP = 3;
23533
23534 /**
23535  * Placement constant - The resizing element is positioned under splitter element
23536  * @static
23537  * @type Number
23538  */
23539 Roo.SplitBar.BOTTOM = 4;
23540 /*
23541  * Based on:
23542  * Ext JS Library 1.1.1
23543  * Copyright(c) 2006-2007, Ext JS, LLC.
23544  *
23545  * Originally Released Under LGPL - original licence link has changed is not relivant.
23546  *
23547  * Fork - LGPL
23548  * <script type="text/javascript">
23549  */
23550
23551 /**
23552  * @class Roo.View
23553  * @extends Roo.util.Observable
23554  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23555  * This class also supports single and multi selection modes. <br>
23556  * Create a data model bound view:
23557  <pre><code>
23558  var store = new Roo.data.Store(...);
23559
23560  var view = new Roo.View({
23561     el : "my-element",
23562     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23563  
23564     singleSelect: true,
23565     selectedClass: "ydataview-selected",
23566     store: store
23567  });
23568
23569  // listen for node click?
23570  view.on("click", function(vw, index, node, e){
23571  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23572  });
23573
23574  // load XML data
23575  dataModel.load("foobar.xml");
23576  </code></pre>
23577  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23578  * <br><br>
23579  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23580  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23581  * 
23582  * Note: old style constructor is still suported (container, template, config)
23583  * 
23584  * @constructor
23585  * Create a new View
23586  * @param {Object} config The config object
23587  * 
23588  */
23589 Roo.View = function(config, depreciated_tpl, depreciated_config){
23590     
23591     if (typeof(depreciated_tpl) == 'undefined') {
23592         // new way.. - universal constructor.
23593         Roo.apply(this, config);
23594         this.el  = Roo.get(this.el);
23595     } else {
23596         // old format..
23597         this.el  = Roo.get(config);
23598         this.tpl = depreciated_tpl;
23599         Roo.apply(this, depreciated_config);
23600     }
23601      
23602     
23603     if(typeof(this.tpl) == "string"){
23604         this.tpl = new Roo.Template(this.tpl);
23605     } else {
23606         // support xtype ctors..
23607         this.tpl = new Roo.factory(this.tpl, Roo);
23608     }
23609     
23610     
23611     this.tpl.compile();
23612    
23613
23614      
23615     /** @private */
23616     this.addEvents({
23617         /**
23618          * @event beforeclick
23619          * Fires before a click is processed. Returns false to cancel the default action.
23620          * @param {Roo.View} this
23621          * @param {Number} index The index of the target node
23622          * @param {HTMLElement} node The target node
23623          * @param {Roo.EventObject} e The raw event object
23624          */
23625             "beforeclick" : true,
23626         /**
23627          * @event click
23628          * Fires when a template node is clicked.
23629          * @param {Roo.View} this
23630          * @param {Number} index The index of the target node
23631          * @param {HTMLElement} node The target node
23632          * @param {Roo.EventObject} e The raw event object
23633          */
23634             "click" : true,
23635         /**
23636          * @event dblclick
23637          * Fires when a template node is double clicked.
23638          * @param {Roo.View} this
23639          * @param {Number} index The index of the target node
23640          * @param {HTMLElement} node The target node
23641          * @param {Roo.EventObject} e The raw event object
23642          */
23643             "dblclick" : true,
23644         /**
23645          * @event contextmenu
23646          * Fires when a template node is right clicked.
23647          * @param {Roo.View} this
23648          * @param {Number} index The index of the target node
23649          * @param {HTMLElement} node The target node
23650          * @param {Roo.EventObject} e The raw event object
23651          */
23652             "contextmenu" : true,
23653         /**
23654          * @event selectionchange
23655          * Fires when the selected nodes change.
23656          * @param {Roo.View} this
23657          * @param {Array} selections Array of the selected nodes
23658          */
23659             "selectionchange" : true,
23660     
23661         /**
23662          * @event beforeselect
23663          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23664          * @param {Roo.View} this
23665          * @param {HTMLElement} node The node to be selected
23666          * @param {Array} selections Array of currently selected nodes
23667          */
23668             "beforeselect" : true,
23669         /**
23670          * @event preparedata
23671          * Fires on every row to render, to allow you to change the data.
23672          * @param {Roo.View} this
23673          * @param {Object} data to be rendered (change this)
23674          */
23675           "preparedata" : true
23676         });
23677
23678     this.el.on({
23679         "click": this.onClick,
23680         "dblclick": this.onDblClick,
23681         "contextmenu": this.onContextMenu,
23682         scope:this
23683     });
23684
23685     this.selections = [];
23686     this.nodes = [];
23687     this.cmp = new Roo.CompositeElementLite([]);
23688     if(this.store){
23689         this.store = Roo.factory(this.store, Roo.data);
23690         this.setStore(this.store, true);
23691     }
23692     Roo.View.superclass.constructor.call(this);
23693 };
23694
23695 Roo.extend(Roo.View, Roo.util.Observable, {
23696     
23697      /**
23698      * @cfg {Roo.data.Store} store Data store to load data from.
23699      */
23700     store : false,
23701     
23702     /**
23703      * @cfg {String|Roo.Element} el The container element.
23704      */
23705     el : '',
23706     
23707     /**
23708      * @cfg {String|Roo.Template} tpl The template used by this View 
23709      */
23710     tpl : false,
23711     
23712     /**
23713      * @cfg {String} selectedClass The css class to add to selected nodes
23714      */
23715     selectedClass : "x-view-selected",
23716      /**
23717      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23718      */
23719     emptyText : "",
23720     /**
23721      * @cfg {Boolean} multiSelect Allow multiple selection
23722      */
23723     
23724     multiSelect : false,
23725     /**
23726      * @cfg {Boolean} singleSelect Allow single selection
23727      */
23728     singleSelect:  false,
23729     
23730     /**
23731      * Returns the element this view is bound to.
23732      * @return {Roo.Element}
23733      */
23734     getEl : function(){
23735         return this.el;
23736     },
23737
23738     /**
23739      * Refreshes the view.
23740      */
23741     refresh : function(){
23742         var t = this.tpl;
23743         this.clearSelections();
23744         this.el.update("");
23745         var html = [];
23746         var records = this.store.getRange();
23747         if(records.length < 1){
23748             this.el.update(this.emptyText);
23749             return;
23750         }
23751         for(var i = 0, len = records.length; i < len; i++){
23752             var data = this.prepareData(records[i].data, i, records[i]);
23753             this.fireEvent("preparedata", this, data, i, records[i]);
23754             html[html.length] = t.apply(data);
23755         }
23756         this.el.update(html.join(""));
23757         this.nodes = this.el.dom.childNodes;
23758         this.updateIndexes(0);
23759     },
23760
23761     /**
23762      * Function to override to reformat the data that is sent to
23763      * the template for each node.
23764      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23765      * a JSON object for an UpdateManager bound view).
23766      */
23767     prepareData : function(data){
23768         return data;
23769     },
23770
23771     onUpdate : function(ds, record){
23772         this.clearSelections();
23773         var index = this.store.indexOf(record);
23774         var n = this.nodes[index];
23775         this.tpl.insertBefore(n, this.prepareData(record.data));
23776         n.parentNode.removeChild(n);
23777         this.updateIndexes(index, index);
23778     },
23779
23780     onAdd : function(ds, records, index){
23781         this.clearSelections();
23782         if(this.nodes.length == 0){
23783             this.refresh();
23784             return;
23785         }
23786         var n = this.nodes[index];
23787         for(var i = 0, len = records.length; i < len; i++){
23788             var d = this.prepareData(records[i].data);
23789             if(n){
23790                 this.tpl.insertBefore(n, d);
23791             }else{
23792                 this.tpl.append(this.el, d);
23793             }
23794         }
23795         this.updateIndexes(index);
23796     },
23797
23798     onRemove : function(ds, record, index){
23799         this.clearSelections();
23800         this.el.dom.removeChild(this.nodes[index]);
23801         this.updateIndexes(index);
23802     },
23803
23804     /**
23805      * Refresh an individual node.
23806      * @param {Number} index
23807      */
23808     refreshNode : function(index){
23809         this.onUpdate(this.store, this.store.getAt(index));
23810     },
23811
23812     updateIndexes : function(startIndex, endIndex){
23813         var ns = this.nodes;
23814         startIndex = startIndex || 0;
23815         endIndex = endIndex || ns.length - 1;
23816         for(var i = startIndex; i <= endIndex; i++){
23817             ns[i].nodeIndex = i;
23818         }
23819     },
23820
23821     /**
23822      * Changes the data store this view uses and refresh the view.
23823      * @param {Store} store
23824      */
23825     setStore : function(store, initial){
23826         if(!initial && this.store){
23827             this.store.un("datachanged", this.refresh);
23828             this.store.un("add", this.onAdd);
23829             this.store.un("remove", this.onRemove);
23830             this.store.un("update", this.onUpdate);
23831             this.store.un("clear", this.refresh);
23832         }
23833         if(store){
23834           
23835             store.on("datachanged", this.refresh, this);
23836             store.on("add", this.onAdd, this);
23837             store.on("remove", this.onRemove, this);
23838             store.on("update", this.onUpdate, this);
23839             store.on("clear", this.refresh, this);
23840         }
23841         
23842         if(store){
23843             this.refresh();
23844         }
23845     },
23846
23847     /**
23848      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23849      * @param {HTMLElement} node
23850      * @return {HTMLElement} The template node
23851      */
23852     findItemFromChild : function(node){
23853         var el = this.el.dom;
23854         if(!node || node.parentNode == el){
23855                     return node;
23856             }
23857             var p = node.parentNode;
23858             while(p && p != el){
23859             if(p.parentNode == el){
23860                 return p;
23861             }
23862             p = p.parentNode;
23863         }
23864             return null;
23865     },
23866
23867     /** @ignore */
23868     onClick : function(e){
23869         var item = this.findItemFromChild(e.getTarget());
23870         if(item){
23871             var index = this.indexOf(item);
23872             if(this.onItemClick(item, index, e) !== false){
23873                 this.fireEvent("click", this, index, item, e);
23874             }
23875         }else{
23876             this.clearSelections();
23877         }
23878     },
23879
23880     /** @ignore */
23881     onContextMenu : function(e){
23882         var item = this.findItemFromChild(e.getTarget());
23883         if(item){
23884             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23885         }
23886     },
23887
23888     /** @ignore */
23889     onDblClick : function(e){
23890         var item = this.findItemFromChild(e.getTarget());
23891         if(item){
23892             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23893         }
23894     },
23895
23896     onItemClick : function(item, index, e){
23897         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23898             return false;
23899         }
23900         if(this.multiSelect || this.singleSelect){
23901             if(this.multiSelect && e.shiftKey && this.lastSelection){
23902                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23903             }else{
23904                 this.select(item, this.multiSelect && e.ctrlKey);
23905                 this.lastSelection = item;
23906             }
23907             e.preventDefault();
23908         }
23909         return true;
23910     },
23911
23912     /**
23913      * Get the number of selected nodes.
23914      * @return {Number}
23915      */
23916     getSelectionCount : function(){
23917         return this.selections.length;
23918     },
23919
23920     /**
23921      * Get the currently selected nodes.
23922      * @return {Array} An array of HTMLElements
23923      */
23924     getSelectedNodes : function(){
23925         return this.selections;
23926     },
23927
23928     /**
23929      * Get the indexes of the selected nodes.
23930      * @return {Array}
23931      */
23932     getSelectedIndexes : function(){
23933         var indexes = [], s = this.selections;
23934         for(var i = 0, len = s.length; i < len; i++){
23935             indexes.push(s[i].nodeIndex);
23936         }
23937         return indexes;
23938     },
23939
23940     /**
23941      * Clear all selections
23942      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23943      */
23944     clearSelections : function(suppressEvent){
23945         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23946             this.cmp.elements = this.selections;
23947             this.cmp.removeClass(this.selectedClass);
23948             this.selections = [];
23949             if(!suppressEvent){
23950                 this.fireEvent("selectionchange", this, this.selections);
23951             }
23952         }
23953     },
23954
23955     /**
23956      * Returns true if the passed node is selected
23957      * @param {HTMLElement/Number} node The node or node index
23958      * @return {Boolean}
23959      */
23960     isSelected : function(node){
23961         var s = this.selections;
23962         if(s.length < 1){
23963             return false;
23964         }
23965         node = this.getNode(node);
23966         return s.indexOf(node) !== -1;
23967     },
23968
23969     /**
23970      * Selects nodes.
23971      * @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
23972      * @param {Boolean} keepExisting (optional) true to keep existing selections
23973      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23974      */
23975     select : function(nodeInfo, keepExisting, suppressEvent){
23976         if(nodeInfo instanceof Array){
23977             if(!keepExisting){
23978                 this.clearSelections(true);
23979             }
23980             for(var i = 0, len = nodeInfo.length; i < len; i++){
23981                 this.select(nodeInfo[i], true, true);
23982             }
23983         } else{
23984             var node = this.getNode(nodeInfo);
23985             if(node && !this.isSelected(node)){
23986                 if(!keepExisting){
23987                     this.clearSelections(true);
23988                 }
23989                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23990                     Roo.fly(node).addClass(this.selectedClass);
23991                     this.selections.push(node);
23992                     if(!suppressEvent){
23993                         this.fireEvent("selectionchange", this, this.selections);
23994                     }
23995                 }
23996             }
23997         }
23998     },
23999
24000     /**
24001      * Gets a template node.
24002      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24003      * @return {HTMLElement} The node or null if it wasn't found
24004      */
24005     getNode : function(nodeInfo){
24006         if(typeof nodeInfo == "string"){
24007             return document.getElementById(nodeInfo);
24008         }else if(typeof nodeInfo == "number"){
24009             return this.nodes[nodeInfo];
24010         }
24011         return nodeInfo;
24012     },
24013
24014     /**
24015      * Gets a range template nodes.
24016      * @param {Number} startIndex
24017      * @param {Number} endIndex
24018      * @return {Array} An array of nodes
24019      */
24020     getNodes : function(start, end){
24021         var ns = this.nodes;
24022         start = start || 0;
24023         end = typeof end == "undefined" ? ns.length - 1 : end;
24024         var nodes = [];
24025         if(start <= end){
24026             for(var i = start; i <= end; i++){
24027                 nodes.push(ns[i]);
24028             }
24029         } else{
24030             for(var i = start; i >= end; i--){
24031                 nodes.push(ns[i]);
24032             }
24033         }
24034         return nodes;
24035     },
24036
24037     /**
24038      * Finds the index of the passed node
24039      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24040      * @return {Number} The index of the node or -1
24041      */
24042     indexOf : function(node){
24043         node = this.getNode(node);
24044         if(typeof node.nodeIndex == "number"){
24045             return node.nodeIndex;
24046         }
24047         var ns = this.nodes;
24048         for(var i = 0, len = ns.length; i < len; i++){
24049             if(ns[i] == node){
24050                 return i;
24051             }
24052         }
24053         return -1;
24054     }
24055 });
24056 /*
24057  * Based on:
24058  * Ext JS Library 1.1.1
24059  * Copyright(c) 2006-2007, Ext JS, LLC.
24060  *
24061  * Originally Released Under LGPL - original licence link has changed is not relivant.
24062  *
24063  * Fork - LGPL
24064  * <script type="text/javascript">
24065  */
24066
24067 /**
24068  * @class Roo.JsonView
24069  * @extends Roo.View
24070  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24071 <pre><code>
24072 var view = new Roo.JsonView({
24073     container: "my-element",
24074     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24075     multiSelect: true, 
24076     jsonRoot: "data" 
24077 });
24078
24079 // listen for node click?
24080 view.on("click", function(vw, index, node, e){
24081     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24082 });
24083
24084 // direct load of JSON data
24085 view.load("foobar.php");
24086
24087 // Example from my blog list
24088 var tpl = new Roo.Template(
24089     '&lt;div class="entry"&gt;' +
24090     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24091     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24092     "&lt;/div&gt;&lt;hr /&gt;"
24093 );
24094
24095 var moreView = new Roo.JsonView({
24096     container :  "entry-list", 
24097     template : tpl,
24098     jsonRoot: "posts"
24099 });
24100 moreView.on("beforerender", this.sortEntries, this);
24101 moreView.load({
24102     url: "/blog/get-posts.php",
24103     params: "allposts=true",
24104     text: "Loading Blog Entries..."
24105 });
24106 </code></pre>
24107
24108 * Note: old code is supported with arguments : (container, template, config)
24109
24110
24111  * @constructor
24112  * Create a new JsonView
24113  * 
24114  * @param {Object} config The config object
24115  * 
24116  */
24117 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24118     
24119     
24120     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24121
24122     var um = this.el.getUpdateManager();
24123     um.setRenderer(this);
24124     um.on("update", this.onLoad, this);
24125     um.on("failure", this.onLoadException, this);
24126
24127     /**
24128      * @event beforerender
24129      * Fires before rendering of the downloaded JSON data.
24130      * @param {Roo.JsonView} this
24131      * @param {Object} data The JSON data loaded
24132      */
24133     /**
24134      * @event load
24135      * Fires when data is loaded.
24136      * @param {Roo.JsonView} this
24137      * @param {Object} data The JSON data loaded
24138      * @param {Object} response The raw Connect response object
24139      */
24140     /**
24141      * @event loadexception
24142      * Fires when loading fails.
24143      * @param {Roo.JsonView} this
24144      * @param {Object} response The raw Connect response object
24145      */
24146     this.addEvents({
24147         'beforerender' : true,
24148         'load' : true,
24149         'loadexception' : true
24150     });
24151 };
24152 Roo.extend(Roo.JsonView, Roo.View, {
24153     /**
24154      * @type {String} The root property in the loaded JSON object that contains the data
24155      */
24156     jsonRoot : "",
24157
24158     /**
24159      * Refreshes the view.
24160      */
24161     refresh : function(){
24162         this.clearSelections();
24163         this.el.update("");
24164         var html = [];
24165         var o = this.jsonData;
24166         if(o && o.length > 0){
24167             for(var i = 0, len = o.length; i < len; i++){
24168                 var data = this.prepareData(o[i], i, o);
24169                 html[html.length] = this.tpl.apply(data);
24170             }
24171         }else{
24172             html.push(this.emptyText);
24173         }
24174         this.el.update(html.join(""));
24175         this.nodes = this.el.dom.childNodes;
24176         this.updateIndexes(0);
24177     },
24178
24179     /**
24180      * 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.
24181      * @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:
24182      <pre><code>
24183      view.load({
24184          url: "your-url.php",
24185          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24186          callback: yourFunction,
24187          scope: yourObject, //(optional scope)
24188          discardUrl: false,
24189          nocache: false,
24190          text: "Loading...",
24191          timeout: 30,
24192          scripts: false
24193      });
24194      </code></pre>
24195      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24196      * 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.
24197      * @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}
24198      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24199      * @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.
24200      */
24201     load : function(){
24202         var um = this.el.getUpdateManager();
24203         um.update.apply(um, arguments);
24204     },
24205
24206     render : function(el, response){
24207         this.clearSelections();
24208         this.el.update("");
24209         var o;
24210         try{
24211             o = Roo.util.JSON.decode(response.responseText);
24212             if(this.jsonRoot){
24213                 
24214                 o = o[this.jsonRoot];
24215             }
24216         } catch(e){
24217         }
24218         /**
24219          * The current JSON data or null
24220          */
24221         this.jsonData = o;
24222         this.beforeRender();
24223         this.refresh();
24224     },
24225
24226 /**
24227  * Get the number of records in the current JSON dataset
24228  * @return {Number}
24229  */
24230     getCount : function(){
24231         return this.jsonData ? this.jsonData.length : 0;
24232     },
24233
24234 /**
24235  * Returns the JSON object for the specified node(s)
24236  * @param {HTMLElement/Array} node The node or an array of nodes
24237  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24238  * you get the JSON object for the node
24239  */
24240     getNodeData : function(node){
24241         if(node instanceof Array){
24242             var data = [];
24243             for(var i = 0, len = node.length; i < len; i++){
24244                 data.push(this.getNodeData(node[i]));
24245             }
24246             return data;
24247         }
24248         return this.jsonData[this.indexOf(node)] || null;
24249     },
24250
24251     beforeRender : function(){
24252         this.snapshot = this.jsonData;
24253         if(this.sortInfo){
24254             this.sort.apply(this, this.sortInfo);
24255         }
24256         this.fireEvent("beforerender", this, this.jsonData);
24257     },
24258
24259     onLoad : function(el, o){
24260         this.fireEvent("load", this, this.jsonData, o);
24261     },
24262
24263     onLoadException : function(el, o){
24264         this.fireEvent("loadexception", this, o);
24265     },
24266
24267 /**
24268  * Filter the data by a specific property.
24269  * @param {String} property A property on your JSON objects
24270  * @param {String/RegExp} value Either string that the property values
24271  * should start with, or a RegExp to test against the property
24272  */
24273     filter : function(property, value){
24274         if(this.jsonData){
24275             var data = [];
24276             var ss = this.snapshot;
24277             if(typeof value == "string"){
24278                 var vlen = value.length;
24279                 if(vlen == 0){
24280                     this.clearFilter();
24281                     return;
24282                 }
24283                 value = value.toLowerCase();
24284                 for(var i = 0, len = ss.length; i < len; i++){
24285                     var o = ss[i];
24286                     if(o[property].substr(0, vlen).toLowerCase() == value){
24287                         data.push(o);
24288                     }
24289                 }
24290             } else if(value.exec){ // regex?
24291                 for(var i = 0, len = ss.length; i < len; i++){
24292                     var o = ss[i];
24293                     if(value.test(o[property])){
24294                         data.push(o);
24295                     }
24296                 }
24297             } else{
24298                 return;
24299             }
24300             this.jsonData = data;
24301             this.refresh();
24302         }
24303     },
24304
24305 /**
24306  * Filter by a function. The passed function will be called with each
24307  * object in the current dataset. If the function returns true the value is kept,
24308  * otherwise it is filtered.
24309  * @param {Function} fn
24310  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24311  */
24312     filterBy : function(fn, scope){
24313         if(this.jsonData){
24314             var data = [];
24315             var ss = this.snapshot;
24316             for(var i = 0, len = ss.length; i < len; i++){
24317                 var o = ss[i];
24318                 if(fn.call(scope || this, o)){
24319                     data.push(o);
24320                 }
24321             }
24322             this.jsonData = data;
24323             this.refresh();
24324         }
24325     },
24326
24327 /**
24328  * Clears the current filter.
24329  */
24330     clearFilter : function(){
24331         if(this.snapshot && this.jsonData != this.snapshot){
24332             this.jsonData = this.snapshot;
24333             this.refresh();
24334         }
24335     },
24336
24337
24338 /**
24339  * Sorts the data for this view and refreshes it.
24340  * @param {String} property A property on your JSON objects to sort on
24341  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24342  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24343  */
24344     sort : function(property, dir, sortType){
24345         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24346         if(this.jsonData){
24347             var p = property;
24348             var dsc = dir && dir.toLowerCase() == "desc";
24349             var f = function(o1, o2){
24350                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24351                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24352                 ;
24353                 if(v1 < v2){
24354                     return dsc ? +1 : -1;
24355                 } else if(v1 > v2){
24356                     return dsc ? -1 : +1;
24357                 } else{
24358                     return 0;
24359                 }
24360             };
24361             this.jsonData.sort(f);
24362             this.refresh();
24363             if(this.jsonData != this.snapshot){
24364                 this.snapshot.sort(f);
24365             }
24366         }
24367     }
24368 });/*
24369  * Based on:
24370  * Ext JS Library 1.1.1
24371  * Copyright(c) 2006-2007, Ext JS, LLC.
24372  *
24373  * Originally Released Under LGPL - original licence link has changed is not relivant.
24374  *
24375  * Fork - LGPL
24376  * <script type="text/javascript">
24377  */
24378  
24379
24380 /**
24381  * @class Roo.ColorPalette
24382  * @extends Roo.Component
24383  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24384  * Here's an example of typical usage:
24385  * <pre><code>
24386 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24387 cp.render('my-div');
24388
24389 cp.on('select', function(palette, selColor){
24390     // do something with selColor
24391 });
24392 </code></pre>
24393  * @constructor
24394  * Create a new ColorPalette
24395  * @param {Object} config The config object
24396  */
24397 Roo.ColorPalette = function(config){
24398     Roo.ColorPalette.superclass.constructor.call(this, config);
24399     this.addEvents({
24400         /**
24401              * @event select
24402              * Fires when a color is selected
24403              * @param {ColorPalette} this
24404              * @param {String} color The 6-digit color hex code (without the # symbol)
24405              */
24406         select: true
24407     });
24408
24409     if(this.handler){
24410         this.on("select", this.handler, this.scope, true);
24411     }
24412 };
24413 Roo.extend(Roo.ColorPalette, Roo.Component, {
24414     /**
24415      * @cfg {String} itemCls
24416      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24417      */
24418     itemCls : "x-color-palette",
24419     /**
24420      * @cfg {String} value
24421      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24422      * the hex codes are case-sensitive.
24423      */
24424     value : null,
24425     clickEvent:'click',
24426     // private
24427     ctype: "Roo.ColorPalette",
24428
24429     /**
24430      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24431      */
24432     allowReselect : false,
24433
24434     /**
24435      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24436      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24437      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24438      * of colors with the width setting until the box is symmetrical.</p>
24439      * <p>You can override individual colors if needed:</p>
24440      * <pre><code>
24441 var cp = new Roo.ColorPalette();
24442 cp.colors[0] = "FF0000";  // change the first box to red
24443 </code></pre>
24444
24445 Or you can provide a custom array of your own for complete control:
24446 <pre><code>
24447 var cp = new Roo.ColorPalette();
24448 cp.colors = ["000000", "993300", "333300"];
24449 </code></pre>
24450      * @type Array
24451      */
24452     colors : [
24453         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24454         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24455         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24456         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24457         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24458     ],
24459
24460     // private
24461     onRender : function(container, position){
24462         var t = new Roo.MasterTemplate(
24463             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24464         );
24465         var c = this.colors;
24466         for(var i = 0, len = c.length; i < len; i++){
24467             t.add([c[i]]);
24468         }
24469         var el = document.createElement("div");
24470         el.className = this.itemCls;
24471         t.overwrite(el);
24472         container.dom.insertBefore(el, position);
24473         this.el = Roo.get(el);
24474         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24475         if(this.clickEvent != 'click'){
24476             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24477         }
24478     },
24479
24480     // private
24481     afterRender : function(){
24482         Roo.ColorPalette.superclass.afterRender.call(this);
24483         if(this.value){
24484             var s = this.value;
24485             this.value = null;
24486             this.select(s);
24487         }
24488     },
24489
24490     // private
24491     handleClick : function(e, t){
24492         e.preventDefault();
24493         if(!this.disabled){
24494             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24495             this.select(c.toUpperCase());
24496         }
24497     },
24498
24499     /**
24500      * Selects the specified color in the palette (fires the select event)
24501      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24502      */
24503     select : function(color){
24504         color = color.replace("#", "");
24505         if(color != this.value || this.allowReselect){
24506             var el = this.el;
24507             if(this.value){
24508                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24509             }
24510             el.child("a.color-"+color).addClass("x-color-palette-sel");
24511             this.value = color;
24512             this.fireEvent("select", this, color);
24513         }
24514     }
24515 });/*
24516  * Based on:
24517  * Ext JS Library 1.1.1
24518  * Copyright(c) 2006-2007, Ext JS, LLC.
24519  *
24520  * Originally Released Under LGPL - original licence link has changed is not relivant.
24521  *
24522  * Fork - LGPL
24523  * <script type="text/javascript">
24524  */
24525  
24526 /**
24527  * @class Roo.DatePicker
24528  * @extends Roo.Component
24529  * Simple date picker class.
24530  * @constructor
24531  * Create a new DatePicker
24532  * @param {Object} config The config object
24533  */
24534 Roo.DatePicker = function(config){
24535     Roo.DatePicker.superclass.constructor.call(this, config);
24536
24537     this.value = config && config.value ?
24538                  config.value.clearTime() : new Date().clearTime();
24539
24540     this.addEvents({
24541         /**
24542              * @event select
24543              * Fires when a date is selected
24544              * @param {DatePicker} this
24545              * @param {Date} date The selected date
24546              */
24547         select: true
24548     });
24549
24550     if(this.handler){
24551         this.on("select", this.handler,  this.scope || this);
24552     }
24553     // build the disabledDatesRE
24554     if(!this.disabledDatesRE && this.disabledDates){
24555         var dd = this.disabledDates;
24556         var re = "(?:";
24557         for(var i = 0; i < dd.length; i++){
24558             re += dd[i];
24559             if(i != dd.length-1) re += "|";
24560         }
24561         this.disabledDatesRE = new RegExp(re + ")");
24562     }
24563 };
24564
24565 Roo.extend(Roo.DatePicker, Roo.Component, {
24566     /**
24567      * @cfg {String} todayText
24568      * The text to display on the button that selects the current date (defaults to "Today")
24569      */
24570     todayText : "Today",
24571     /**
24572      * @cfg {String} okText
24573      * The text to display on the ok button
24574      */
24575     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24576     /**
24577      * @cfg {String} cancelText
24578      * The text to display on the cancel button
24579      */
24580     cancelText : "Cancel",
24581     /**
24582      * @cfg {String} todayTip
24583      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24584      */
24585     todayTip : "{0} (Spacebar)",
24586     /**
24587      * @cfg {Date} minDate
24588      * Minimum allowable date (JavaScript date object, defaults to null)
24589      */
24590     minDate : null,
24591     /**
24592      * @cfg {Date} maxDate
24593      * Maximum allowable date (JavaScript date object, defaults to null)
24594      */
24595     maxDate : null,
24596     /**
24597      * @cfg {String} minText
24598      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24599      */
24600     minText : "This date is before the minimum date",
24601     /**
24602      * @cfg {String} maxText
24603      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24604      */
24605     maxText : "This date is after the maximum date",
24606     /**
24607      * @cfg {String} format
24608      * The default date format string which can be overriden for localization support.  The format must be
24609      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24610      */
24611     format : "m/d/y",
24612     /**
24613      * @cfg {Array} disabledDays
24614      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24615      */
24616     disabledDays : null,
24617     /**
24618      * @cfg {String} disabledDaysText
24619      * The tooltip to display when the date falls on a disabled day (defaults to "")
24620      */
24621     disabledDaysText : "",
24622     /**
24623      * @cfg {RegExp} disabledDatesRE
24624      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24625      */
24626     disabledDatesRE : null,
24627     /**
24628      * @cfg {String} disabledDatesText
24629      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24630      */
24631     disabledDatesText : "",
24632     /**
24633      * @cfg {Boolean} constrainToViewport
24634      * True to constrain the date picker to the viewport (defaults to true)
24635      */
24636     constrainToViewport : true,
24637     /**
24638      * @cfg {Array} monthNames
24639      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24640      */
24641     monthNames : Date.monthNames,
24642     /**
24643      * @cfg {Array} dayNames
24644      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24645      */
24646     dayNames : Date.dayNames,
24647     /**
24648      * @cfg {String} nextText
24649      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24650      */
24651     nextText: 'Next Month (Control+Right)',
24652     /**
24653      * @cfg {String} prevText
24654      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24655      */
24656     prevText: 'Previous Month (Control+Left)',
24657     /**
24658      * @cfg {String} monthYearText
24659      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24660      */
24661     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24662     /**
24663      * @cfg {Number} startDay
24664      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24665      */
24666     startDay : 0,
24667     /**
24668      * @cfg {Bool} showClear
24669      * Show a clear button (usefull for date form elements that can be blank.)
24670      */
24671     
24672     showClear: false,
24673     
24674     /**
24675      * Sets the value of the date field
24676      * @param {Date} value The date to set
24677      */
24678     setValue : function(value){
24679         var old = this.value;
24680         this.value = value.clearTime(true);
24681         if(this.el){
24682             this.update(this.value);
24683         }
24684     },
24685
24686     /**
24687      * Gets the current selected value of the date field
24688      * @return {Date} The selected date
24689      */
24690     getValue : function(){
24691         return this.value;
24692     },
24693
24694     // private
24695     focus : function(){
24696         if(this.el){
24697             this.update(this.activeDate);
24698         }
24699     },
24700
24701     // private
24702     onRender : function(container, position){
24703         var m = [
24704              '<table cellspacing="0">',
24705                 '<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>',
24706                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24707         var dn = this.dayNames;
24708         for(var i = 0; i < 7; i++){
24709             var d = this.startDay+i;
24710             if(d > 6){
24711                 d = d-7;
24712             }
24713             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24714         }
24715         m[m.length] = "</tr></thead><tbody><tr>";
24716         for(var i = 0; i < 42; i++) {
24717             if(i % 7 == 0 && i != 0){
24718                 m[m.length] = "</tr><tr>";
24719             }
24720             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24721         }
24722         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24723             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24724
24725         var el = document.createElement("div");
24726         el.className = "x-date-picker";
24727         el.innerHTML = m.join("");
24728
24729         container.dom.insertBefore(el, position);
24730
24731         this.el = Roo.get(el);
24732         this.eventEl = Roo.get(el.firstChild);
24733
24734         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24735             handler: this.showPrevMonth,
24736             scope: this,
24737             preventDefault:true,
24738             stopDefault:true
24739         });
24740
24741         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24742             handler: this.showNextMonth,
24743             scope: this,
24744             preventDefault:true,
24745             stopDefault:true
24746         });
24747
24748         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24749
24750         this.monthPicker = this.el.down('div.x-date-mp');
24751         this.monthPicker.enableDisplayMode('block');
24752         
24753         var kn = new Roo.KeyNav(this.eventEl, {
24754             "left" : function(e){
24755                 e.ctrlKey ?
24756                     this.showPrevMonth() :
24757                     this.update(this.activeDate.add("d", -1));
24758             },
24759
24760             "right" : function(e){
24761                 e.ctrlKey ?
24762                     this.showNextMonth() :
24763                     this.update(this.activeDate.add("d", 1));
24764             },
24765
24766             "up" : function(e){
24767                 e.ctrlKey ?
24768                     this.showNextYear() :
24769                     this.update(this.activeDate.add("d", -7));
24770             },
24771
24772             "down" : function(e){
24773                 e.ctrlKey ?
24774                     this.showPrevYear() :
24775                     this.update(this.activeDate.add("d", 7));
24776             },
24777
24778             "pageUp" : function(e){
24779                 this.showNextMonth();
24780             },
24781
24782             "pageDown" : function(e){
24783                 this.showPrevMonth();
24784             },
24785
24786             "enter" : function(e){
24787                 e.stopPropagation();
24788                 return true;
24789             },
24790
24791             scope : this
24792         });
24793
24794         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24795
24796         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24797
24798         this.el.unselectable();
24799         
24800         this.cells = this.el.select("table.x-date-inner tbody td");
24801         this.textNodes = this.el.query("table.x-date-inner tbody span");
24802
24803         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24804             text: "&#160;",
24805             tooltip: this.monthYearText
24806         });
24807
24808         this.mbtn.on('click', this.showMonthPicker, this);
24809         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24810
24811
24812         var today = (new Date()).dateFormat(this.format);
24813         
24814         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24815         if (this.showClear) {
24816             baseTb.add( new Roo.Toolbar.Fill());
24817         }
24818         baseTb.add({
24819             text: String.format(this.todayText, today),
24820             tooltip: String.format(this.todayTip, today),
24821             handler: this.selectToday,
24822             scope: this
24823         });
24824         
24825         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24826             
24827         //});
24828         if (this.showClear) {
24829             
24830             baseTb.add( new Roo.Toolbar.Fill());
24831             baseTb.add({
24832                 text: '&#160;',
24833                 cls: 'x-btn-icon x-btn-clear',
24834                 handler: function() {
24835                     //this.value = '';
24836                     this.fireEvent("select", this, '');
24837                 },
24838                 scope: this
24839             });
24840         }
24841         
24842         
24843         if(Roo.isIE){
24844             this.el.repaint();
24845         }
24846         this.update(this.value);
24847     },
24848
24849     createMonthPicker : function(){
24850         if(!this.monthPicker.dom.firstChild){
24851             var buf = ['<table border="0" cellspacing="0">'];
24852             for(var i = 0; i < 6; i++){
24853                 buf.push(
24854                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24855                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24856                     i == 0 ?
24857                     '<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>' :
24858                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24859                 );
24860             }
24861             buf.push(
24862                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24863                     this.okText,
24864                     '</button><button type="button" class="x-date-mp-cancel">',
24865                     this.cancelText,
24866                     '</button></td></tr>',
24867                 '</table>'
24868             );
24869             this.monthPicker.update(buf.join(''));
24870             this.monthPicker.on('click', this.onMonthClick, this);
24871             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24872
24873             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24874             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24875
24876             this.mpMonths.each(function(m, a, i){
24877                 i += 1;
24878                 if((i%2) == 0){
24879                     m.dom.xmonth = 5 + Math.round(i * .5);
24880                 }else{
24881                     m.dom.xmonth = Math.round((i-1) * .5);
24882                 }
24883             });
24884         }
24885     },
24886
24887     showMonthPicker : function(){
24888         this.createMonthPicker();
24889         var size = this.el.getSize();
24890         this.monthPicker.setSize(size);
24891         this.monthPicker.child('table').setSize(size);
24892
24893         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24894         this.updateMPMonth(this.mpSelMonth);
24895         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24896         this.updateMPYear(this.mpSelYear);
24897
24898         this.monthPicker.slideIn('t', {duration:.2});
24899     },
24900
24901     updateMPYear : function(y){
24902         this.mpyear = y;
24903         var ys = this.mpYears.elements;
24904         for(var i = 1; i <= 10; i++){
24905             var td = ys[i-1], y2;
24906             if((i%2) == 0){
24907                 y2 = y + Math.round(i * .5);
24908                 td.firstChild.innerHTML = y2;
24909                 td.xyear = y2;
24910             }else{
24911                 y2 = y - (5-Math.round(i * .5));
24912                 td.firstChild.innerHTML = y2;
24913                 td.xyear = y2;
24914             }
24915             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24916         }
24917     },
24918
24919     updateMPMonth : function(sm){
24920         this.mpMonths.each(function(m, a, i){
24921             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24922         });
24923     },
24924
24925     selectMPMonth: function(m){
24926         
24927     },
24928
24929     onMonthClick : function(e, t){
24930         e.stopEvent();
24931         var el = new Roo.Element(t), pn;
24932         if(el.is('button.x-date-mp-cancel')){
24933             this.hideMonthPicker();
24934         }
24935         else if(el.is('button.x-date-mp-ok')){
24936             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24937             this.hideMonthPicker();
24938         }
24939         else if(pn = el.up('td.x-date-mp-month', 2)){
24940             this.mpMonths.removeClass('x-date-mp-sel');
24941             pn.addClass('x-date-mp-sel');
24942             this.mpSelMonth = pn.dom.xmonth;
24943         }
24944         else if(pn = el.up('td.x-date-mp-year', 2)){
24945             this.mpYears.removeClass('x-date-mp-sel');
24946             pn.addClass('x-date-mp-sel');
24947             this.mpSelYear = pn.dom.xyear;
24948         }
24949         else if(el.is('a.x-date-mp-prev')){
24950             this.updateMPYear(this.mpyear-10);
24951         }
24952         else if(el.is('a.x-date-mp-next')){
24953             this.updateMPYear(this.mpyear+10);
24954         }
24955     },
24956
24957     onMonthDblClick : function(e, t){
24958         e.stopEvent();
24959         var el = new Roo.Element(t), pn;
24960         if(pn = el.up('td.x-date-mp-month', 2)){
24961             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24962             this.hideMonthPicker();
24963         }
24964         else if(pn = el.up('td.x-date-mp-year', 2)){
24965             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24966             this.hideMonthPicker();
24967         }
24968     },
24969
24970     hideMonthPicker : function(disableAnim){
24971         if(this.monthPicker){
24972             if(disableAnim === true){
24973                 this.monthPicker.hide();
24974             }else{
24975                 this.monthPicker.slideOut('t', {duration:.2});
24976             }
24977         }
24978     },
24979
24980     // private
24981     showPrevMonth : function(e){
24982         this.update(this.activeDate.add("mo", -1));
24983     },
24984
24985     // private
24986     showNextMonth : function(e){
24987         this.update(this.activeDate.add("mo", 1));
24988     },
24989
24990     // private
24991     showPrevYear : function(){
24992         this.update(this.activeDate.add("y", -1));
24993     },
24994
24995     // private
24996     showNextYear : function(){
24997         this.update(this.activeDate.add("y", 1));
24998     },
24999
25000     // private
25001     handleMouseWheel : function(e){
25002         var delta = e.getWheelDelta();
25003         if(delta > 0){
25004             this.showPrevMonth();
25005             e.stopEvent();
25006         } else if(delta < 0){
25007             this.showNextMonth();
25008             e.stopEvent();
25009         }
25010     },
25011
25012     // private
25013     handleDateClick : function(e, t){
25014         e.stopEvent();
25015         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25016             this.setValue(new Date(t.dateValue));
25017             this.fireEvent("select", this, this.value);
25018         }
25019     },
25020
25021     // private
25022     selectToday : function(){
25023         this.setValue(new Date().clearTime());
25024         this.fireEvent("select", this, this.value);
25025     },
25026
25027     // private
25028     update : function(date){
25029         var vd = this.activeDate;
25030         this.activeDate = date;
25031         if(vd && this.el){
25032             var t = date.getTime();
25033             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25034                 this.cells.removeClass("x-date-selected");
25035                 this.cells.each(function(c){
25036                    if(c.dom.firstChild.dateValue == t){
25037                        c.addClass("x-date-selected");
25038                        setTimeout(function(){
25039                             try{c.dom.firstChild.focus();}catch(e){}
25040                        }, 50);
25041                        return false;
25042                    }
25043                 });
25044                 return;
25045             }
25046         }
25047         var days = date.getDaysInMonth();
25048         var firstOfMonth = date.getFirstDateOfMonth();
25049         var startingPos = firstOfMonth.getDay()-this.startDay;
25050
25051         if(startingPos <= this.startDay){
25052             startingPos += 7;
25053         }
25054
25055         var pm = date.add("mo", -1);
25056         var prevStart = pm.getDaysInMonth()-startingPos;
25057
25058         var cells = this.cells.elements;
25059         var textEls = this.textNodes;
25060         days += startingPos;
25061
25062         // convert everything to numbers so it's fast
25063         var day = 86400000;
25064         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25065         var today = new Date().clearTime().getTime();
25066         var sel = date.clearTime().getTime();
25067         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25068         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25069         var ddMatch = this.disabledDatesRE;
25070         var ddText = this.disabledDatesText;
25071         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25072         var ddaysText = this.disabledDaysText;
25073         var format = this.format;
25074
25075         var setCellClass = function(cal, cell){
25076             cell.title = "";
25077             var t = d.getTime();
25078             cell.firstChild.dateValue = t;
25079             if(t == today){
25080                 cell.className += " x-date-today";
25081                 cell.title = cal.todayText;
25082             }
25083             if(t == sel){
25084                 cell.className += " x-date-selected";
25085                 setTimeout(function(){
25086                     try{cell.firstChild.focus();}catch(e){}
25087                 }, 50);
25088             }
25089             // disabling
25090             if(t < min) {
25091                 cell.className = " x-date-disabled";
25092                 cell.title = cal.minText;
25093                 return;
25094             }
25095             if(t > max) {
25096                 cell.className = " x-date-disabled";
25097                 cell.title = cal.maxText;
25098                 return;
25099             }
25100             if(ddays){
25101                 if(ddays.indexOf(d.getDay()) != -1){
25102                     cell.title = ddaysText;
25103                     cell.className = " x-date-disabled";
25104                 }
25105             }
25106             if(ddMatch && format){
25107                 var fvalue = d.dateFormat(format);
25108                 if(ddMatch.test(fvalue)){
25109                     cell.title = ddText.replace("%0", fvalue);
25110                     cell.className = " x-date-disabled";
25111                 }
25112             }
25113         };
25114
25115         var i = 0;
25116         for(; i < startingPos; i++) {
25117             textEls[i].innerHTML = (++prevStart);
25118             d.setDate(d.getDate()+1);
25119             cells[i].className = "x-date-prevday";
25120             setCellClass(this, cells[i]);
25121         }
25122         for(; i < days; i++){
25123             intDay = i - startingPos + 1;
25124             textEls[i].innerHTML = (intDay);
25125             d.setDate(d.getDate()+1);
25126             cells[i].className = "x-date-active";
25127             setCellClass(this, cells[i]);
25128         }
25129         var extraDays = 0;
25130         for(; i < 42; i++) {
25131              textEls[i].innerHTML = (++extraDays);
25132              d.setDate(d.getDate()+1);
25133              cells[i].className = "x-date-nextday";
25134              setCellClass(this, cells[i]);
25135         }
25136
25137         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25138
25139         if(!this.internalRender){
25140             var main = this.el.dom.firstChild;
25141             var w = main.offsetWidth;
25142             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25143             Roo.fly(main).setWidth(w);
25144             this.internalRender = true;
25145             // opera does not respect the auto grow header center column
25146             // then, after it gets a width opera refuses to recalculate
25147             // without a second pass
25148             if(Roo.isOpera && !this.secondPass){
25149                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25150                 this.secondPass = true;
25151                 this.update.defer(10, this, [date]);
25152             }
25153         }
25154     }
25155 });        /*
25156  * Based on:
25157  * Ext JS Library 1.1.1
25158  * Copyright(c) 2006-2007, Ext JS, LLC.
25159  *
25160  * Originally Released Under LGPL - original licence link has changed is not relivant.
25161  *
25162  * Fork - LGPL
25163  * <script type="text/javascript">
25164  */
25165 /**
25166  * @class Roo.TabPanel
25167  * @extends Roo.util.Observable
25168  * A lightweight tab container.
25169  * <br><br>
25170  * Usage:
25171  * <pre><code>
25172 // basic tabs 1, built from existing content
25173 var tabs = new Roo.TabPanel("tabs1");
25174 tabs.addTab("script", "View Script");
25175 tabs.addTab("markup", "View Markup");
25176 tabs.activate("script");
25177
25178 // more advanced tabs, built from javascript
25179 var jtabs = new Roo.TabPanel("jtabs");
25180 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25181
25182 // set up the UpdateManager
25183 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25184 var updater = tab2.getUpdateManager();
25185 updater.setDefaultUrl("ajax1.htm");
25186 tab2.on('activate', updater.refresh, updater, true);
25187
25188 // Use setUrl for Ajax loading
25189 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25190 tab3.setUrl("ajax2.htm", null, true);
25191
25192 // Disabled tab
25193 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25194 tab4.disable();
25195
25196 jtabs.activate("jtabs-1");
25197  * </code></pre>
25198  * @constructor
25199  * Create a new TabPanel.
25200  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25201  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25202  */
25203 Roo.TabPanel = function(container, config){
25204     /**
25205     * The container element for this TabPanel.
25206     * @type Roo.Element
25207     */
25208     this.el = Roo.get(container, true);
25209     if(config){
25210         if(typeof config == "boolean"){
25211             this.tabPosition = config ? "bottom" : "top";
25212         }else{
25213             Roo.apply(this, config);
25214         }
25215     }
25216     if(this.tabPosition == "bottom"){
25217         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25218         this.el.addClass("x-tabs-bottom");
25219     }
25220     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25221     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25222     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25223     if(Roo.isIE){
25224         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25225     }
25226     if(this.tabPosition != "bottom"){
25227         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25228          * @type Roo.Element
25229          */
25230         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25231         this.el.addClass("x-tabs-top");
25232     }
25233     this.items = [];
25234
25235     this.bodyEl.setStyle("position", "relative");
25236
25237     this.active = null;
25238     this.activateDelegate = this.activate.createDelegate(this);
25239
25240     this.addEvents({
25241         /**
25242          * @event tabchange
25243          * Fires when the active tab changes
25244          * @param {Roo.TabPanel} this
25245          * @param {Roo.TabPanelItem} activePanel The new active tab
25246          */
25247         "tabchange": true,
25248         /**
25249          * @event beforetabchange
25250          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25251          * @param {Roo.TabPanel} this
25252          * @param {Object} e Set cancel to true on this object to cancel the tab change
25253          * @param {Roo.TabPanelItem} tab The tab being changed to
25254          */
25255         "beforetabchange" : true
25256     });
25257
25258     Roo.EventManager.onWindowResize(this.onResize, this);
25259     this.cpad = this.el.getPadding("lr");
25260     this.hiddenCount = 0;
25261
25262
25263     // toolbar on the tabbar support...
25264     if (this.toolbar) {
25265         var tcfg = this.toolbar;
25266         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25267         this.toolbar = new Roo.Toolbar(tcfg);
25268         if (Roo.isSafari) {
25269             var tbl = tcfg.container.child('table', true);
25270             tbl.setAttribute('width', '100%');
25271         }
25272         
25273     }
25274    
25275
25276
25277     Roo.TabPanel.superclass.constructor.call(this);
25278 };
25279
25280 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25281     /*
25282      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25283      */
25284     tabPosition : "top",
25285     /*
25286      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25287      */
25288     currentTabWidth : 0,
25289     /*
25290      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25291      */
25292     minTabWidth : 40,
25293     /*
25294      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25295      */
25296     maxTabWidth : 250,
25297     /*
25298      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25299      */
25300     preferredTabWidth : 175,
25301     /*
25302      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25303      */
25304     resizeTabs : false,
25305     /*
25306      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25307      */
25308     monitorResize : true,
25309     /*
25310      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25311      */
25312     toolbar : false,
25313
25314     /**
25315      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25316      * @param {String} id The id of the div to use <b>or create</b>
25317      * @param {String} text The text for the tab
25318      * @param {String} content (optional) Content to put in the TabPanelItem body
25319      * @param {Boolean} closable (optional) True to create a close icon on the tab
25320      * @return {Roo.TabPanelItem} The created TabPanelItem
25321      */
25322     addTab : function(id, text, content, closable){
25323         var item = new Roo.TabPanelItem(this, id, text, closable);
25324         this.addTabItem(item);
25325         if(content){
25326             item.setContent(content);
25327         }
25328         return item;
25329     },
25330
25331     /**
25332      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25333      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25334      * @return {Roo.TabPanelItem}
25335      */
25336     getTab : function(id){
25337         return this.items[id];
25338     },
25339
25340     /**
25341      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25342      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25343      */
25344     hideTab : function(id){
25345         var t = this.items[id];
25346         if(!t.isHidden()){
25347            t.setHidden(true);
25348            this.hiddenCount++;
25349            this.autoSizeTabs();
25350         }
25351     },
25352
25353     /**
25354      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25355      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25356      */
25357     unhideTab : function(id){
25358         var t = this.items[id];
25359         if(t.isHidden()){
25360            t.setHidden(false);
25361            this.hiddenCount--;
25362            this.autoSizeTabs();
25363         }
25364     },
25365
25366     /**
25367      * Adds an existing {@link Roo.TabPanelItem}.
25368      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25369      */
25370     addTabItem : function(item){
25371         this.items[item.id] = item;
25372         this.items.push(item);
25373         if(this.resizeTabs){
25374            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25375            this.autoSizeTabs();
25376         }else{
25377             item.autoSize();
25378         }
25379     },
25380
25381     /**
25382      * Removes a {@link Roo.TabPanelItem}.
25383      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25384      */
25385     removeTab : function(id){
25386         var items = this.items;
25387         var tab = items[id];
25388         if(!tab) { return; }
25389         var index = items.indexOf(tab);
25390         if(this.active == tab && items.length > 1){
25391             var newTab = this.getNextAvailable(index);
25392             if(newTab) {
25393                 newTab.activate();
25394             }
25395         }
25396         this.stripEl.dom.removeChild(tab.pnode.dom);
25397         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25398             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25399         }
25400         items.splice(index, 1);
25401         delete this.items[tab.id];
25402         tab.fireEvent("close", tab);
25403         tab.purgeListeners();
25404         this.autoSizeTabs();
25405     },
25406
25407     getNextAvailable : function(start){
25408         var items = this.items;
25409         var index = start;
25410         // look for a next tab that will slide over to
25411         // replace the one being removed
25412         while(index < items.length){
25413             var item = items[++index];
25414             if(item && !item.isHidden()){
25415                 return item;
25416             }
25417         }
25418         // if one isn't found select the previous tab (on the left)
25419         index = start;
25420         while(index >= 0){
25421             var item = items[--index];
25422             if(item && !item.isHidden()){
25423                 return item;
25424             }
25425         }
25426         return null;
25427     },
25428
25429     /**
25430      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25431      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25432      */
25433     disableTab : function(id){
25434         var tab = this.items[id];
25435         if(tab && this.active != tab){
25436             tab.disable();
25437         }
25438     },
25439
25440     /**
25441      * Enables a {@link Roo.TabPanelItem} that is disabled.
25442      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25443      */
25444     enableTab : function(id){
25445         var tab = this.items[id];
25446         tab.enable();
25447     },
25448
25449     /**
25450      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25451      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25452      * @return {Roo.TabPanelItem} The TabPanelItem.
25453      */
25454     activate : function(id){
25455         var tab = this.items[id];
25456         if(!tab){
25457             return null;
25458         }
25459         if(tab == this.active || tab.disabled){
25460             return tab;
25461         }
25462         var e = {};
25463         this.fireEvent("beforetabchange", this, e, tab);
25464         if(e.cancel !== true && !tab.disabled){
25465             if(this.active){
25466                 this.active.hide();
25467             }
25468             this.active = this.items[id];
25469             this.active.show();
25470             this.fireEvent("tabchange", this, this.active);
25471         }
25472         return tab;
25473     },
25474
25475     /**
25476      * Gets the active {@link Roo.TabPanelItem}.
25477      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25478      */
25479     getActiveTab : function(){
25480         return this.active;
25481     },
25482
25483     /**
25484      * Updates the tab body element to fit the height of the container element
25485      * for overflow scrolling
25486      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25487      */
25488     syncHeight : function(targetHeight){
25489         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25490         var bm = this.bodyEl.getMargins();
25491         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25492         this.bodyEl.setHeight(newHeight);
25493         return newHeight;
25494     },
25495
25496     onResize : function(){
25497         if(this.monitorResize){
25498             this.autoSizeTabs();
25499         }
25500     },
25501
25502     /**
25503      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25504      */
25505     beginUpdate : function(){
25506         this.updating = true;
25507     },
25508
25509     /**
25510      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25511      */
25512     endUpdate : function(){
25513         this.updating = false;
25514         this.autoSizeTabs();
25515     },
25516
25517     /**
25518      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25519      */
25520     autoSizeTabs : function(){
25521         var count = this.items.length;
25522         var vcount = count - this.hiddenCount;
25523         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25524         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25525         var availWidth = Math.floor(w / vcount);
25526         var b = this.stripBody;
25527         if(b.getWidth() > w){
25528             var tabs = this.items;
25529             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25530             if(availWidth < this.minTabWidth){
25531                 /*if(!this.sleft){    // incomplete scrolling code
25532                     this.createScrollButtons();
25533                 }
25534                 this.showScroll();
25535                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25536             }
25537         }else{
25538             if(this.currentTabWidth < this.preferredTabWidth){
25539                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25540             }
25541         }
25542     },
25543
25544     /**
25545      * Returns the number of tabs in this TabPanel.
25546      * @return {Number}
25547      */
25548      getCount : function(){
25549          return this.items.length;
25550      },
25551
25552     /**
25553      * Resizes all the tabs to the passed width
25554      * @param {Number} The new width
25555      */
25556     setTabWidth : function(width){
25557         this.currentTabWidth = width;
25558         for(var i = 0, len = this.items.length; i < len; i++) {
25559                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25560         }
25561     },
25562
25563     /**
25564      * Destroys this TabPanel
25565      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25566      */
25567     destroy : function(removeEl){
25568         Roo.EventManager.removeResizeListener(this.onResize, this);
25569         for(var i = 0, len = this.items.length; i < len; i++){
25570             this.items[i].purgeListeners();
25571         }
25572         if(removeEl === true){
25573             this.el.update("");
25574             this.el.remove();
25575         }
25576     }
25577 });
25578
25579 /**
25580  * @class Roo.TabPanelItem
25581  * @extends Roo.util.Observable
25582  * Represents an individual item (tab plus body) in a TabPanel.
25583  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25584  * @param {String} id The id of this TabPanelItem
25585  * @param {String} text The text for the tab of this TabPanelItem
25586  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25587  */
25588 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25589     /**
25590      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25591      * @type Roo.TabPanel
25592      */
25593     this.tabPanel = tabPanel;
25594     /**
25595      * The id for this TabPanelItem
25596      * @type String
25597      */
25598     this.id = id;
25599     /** @private */
25600     this.disabled = false;
25601     /** @private */
25602     this.text = text;
25603     /** @private */
25604     this.loaded = false;
25605     this.closable = closable;
25606
25607     /**
25608      * The body element for this TabPanelItem.
25609      * @type Roo.Element
25610      */
25611     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25612     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25613     this.bodyEl.setStyle("display", "block");
25614     this.bodyEl.setStyle("zoom", "1");
25615     this.hideAction();
25616
25617     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25618     /** @private */
25619     this.el = Roo.get(els.el, true);
25620     this.inner = Roo.get(els.inner, true);
25621     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25622     this.pnode = Roo.get(els.el.parentNode, true);
25623     this.el.on("mousedown", this.onTabMouseDown, this);
25624     this.el.on("click", this.onTabClick, this);
25625     /** @private */
25626     if(closable){
25627         var c = Roo.get(els.close, true);
25628         c.dom.title = this.closeText;
25629         c.addClassOnOver("close-over");
25630         c.on("click", this.closeClick, this);
25631      }
25632
25633     this.addEvents({
25634          /**
25635          * @event activate
25636          * Fires when this tab becomes the active tab.
25637          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25638          * @param {Roo.TabPanelItem} this
25639          */
25640         "activate": true,
25641         /**
25642          * @event beforeclose
25643          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25644          * @param {Roo.TabPanelItem} this
25645          * @param {Object} e Set cancel to true on this object to cancel the close.
25646          */
25647         "beforeclose": true,
25648         /**
25649          * @event close
25650          * Fires when this tab is closed.
25651          * @param {Roo.TabPanelItem} this
25652          */
25653          "close": true,
25654         /**
25655          * @event deactivate
25656          * Fires when this tab is no longer the active tab.
25657          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25658          * @param {Roo.TabPanelItem} this
25659          */
25660          "deactivate" : true
25661     });
25662     this.hidden = false;
25663
25664     Roo.TabPanelItem.superclass.constructor.call(this);
25665 };
25666
25667 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25668     purgeListeners : function(){
25669        Roo.util.Observable.prototype.purgeListeners.call(this);
25670        this.el.removeAllListeners();
25671     },
25672     /**
25673      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25674      */
25675     show : function(){
25676         this.pnode.addClass("on");
25677         this.showAction();
25678         if(Roo.isOpera){
25679             this.tabPanel.stripWrap.repaint();
25680         }
25681         this.fireEvent("activate", this.tabPanel, this);
25682     },
25683
25684     /**
25685      * Returns true if this tab is the active tab.
25686      * @return {Boolean}
25687      */
25688     isActive : function(){
25689         return this.tabPanel.getActiveTab() == this;
25690     },
25691
25692     /**
25693      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25694      */
25695     hide : function(){
25696         this.pnode.removeClass("on");
25697         this.hideAction();
25698         this.fireEvent("deactivate", this.tabPanel, this);
25699     },
25700
25701     hideAction : function(){
25702         this.bodyEl.hide();
25703         this.bodyEl.setStyle("position", "absolute");
25704         this.bodyEl.setLeft("-20000px");
25705         this.bodyEl.setTop("-20000px");
25706     },
25707
25708     showAction : function(){
25709         this.bodyEl.setStyle("position", "relative");
25710         this.bodyEl.setTop("");
25711         this.bodyEl.setLeft("");
25712         this.bodyEl.show();
25713     },
25714
25715     /**
25716      * Set the tooltip for the tab.
25717      * @param {String} tooltip The tab's tooltip
25718      */
25719     setTooltip : function(text){
25720         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25721             this.textEl.dom.qtip = text;
25722             this.textEl.dom.removeAttribute('title');
25723         }else{
25724             this.textEl.dom.title = text;
25725         }
25726     },
25727
25728     onTabClick : function(e){
25729         e.preventDefault();
25730         this.tabPanel.activate(this.id);
25731     },
25732
25733     onTabMouseDown : function(e){
25734         e.preventDefault();
25735         this.tabPanel.activate(this.id);
25736     },
25737
25738     getWidth : function(){
25739         return this.inner.getWidth();
25740     },
25741
25742     setWidth : function(width){
25743         var iwidth = width - this.pnode.getPadding("lr");
25744         this.inner.setWidth(iwidth);
25745         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25746         this.pnode.setWidth(width);
25747     },
25748
25749     /**
25750      * Show or hide the tab
25751      * @param {Boolean} hidden True to hide or false to show.
25752      */
25753     setHidden : function(hidden){
25754         this.hidden = hidden;
25755         this.pnode.setStyle("display", hidden ? "none" : "");
25756     },
25757
25758     /**
25759      * Returns true if this tab is "hidden"
25760      * @return {Boolean}
25761      */
25762     isHidden : function(){
25763         return this.hidden;
25764     },
25765
25766     /**
25767      * Returns the text for this tab
25768      * @return {String}
25769      */
25770     getText : function(){
25771         return this.text;
25772     },
25773
25774     autoSize : function(){
25775         //this.el.beginMeasure();
25776         this.textEl.setWidth(1);
25777         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25778         //this.el.endMeasure();
25779     },
25780
25781     /**
25782      * Sets the text for the tab (Note: this also sets the tooltip text)
25783      * @param {String} text The tab's text and tooltip
25784      */
25785     setText : function(text){
25786         this.text = text;
25787         this.textEl.update(text);
25788         this.setTooltip(text);
25789         if(!this.tabPanel.resizeTabs){
25790             this.autoSize();
25791         }
25792     },
25793     /**
25794      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25795      */
25796     activate : function(){
25797         this.tabPanel.activate(this.id);
25798     },
25799
25800     /**
25801      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25802      */
25803     disable : function(){
25804         if(this.tabPanel.active != this){
25805             this.disabled = true;
25806             this.pnode.addClass("disabled");
25807         }
25808     },
25809
25810     /**
25811      * Enables this TabPanelItem if it was previously disabled.
25812      */
25813     enable : function(){
25814         this.disabled = false;
25815         this.pnode.removeClass("disabled");
25816     },
25817
25818     /**
25819      * Sets the content for this TabPanelItem.
25820      * @param {String} content The content
25821      * @param {Boolean} loadScripts true to look for and load scripts
25822      */
25823     setContent : function(content, loadScripts){
25824         this.bodyEl.update(content, loadScripts);
25825     },
25826
25827     /**
25828      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25829      * @return {Roo.UpdateManager} The UpdateManager
25830      */
25831     getUpdateManager : function(){
25832         return this.bodyEl.getUpdateManager();
25833     },
25834
25835     /**
25836      * Set a URL to be used to load the content for this TabPanelItem.
25837      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25838      * @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)
25839      * @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)
25840      * @return {Roo.UpdateManager} The UpdateManager
25841      */
25842     setUrl : function(url, params, loadOnce){
25843         if(this.refreshDelegate){
25844             this.un('activate', this.refreshDelegate);
25845         }
25846         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25847         this.on("activate", this.refreshDelegate);
25848         return this.bodyEl.getUpdateManager();
25849     },
25850
25851     /** @private */
25852     _handleRefresh : function(url, params, loadOnce){
25853         if(!loadOnce || !this.loaded){
25854             var updater = this.bodyEl.getUpdateManager();
25855             updater.update(url, params, this._setLoaded.createDelegate(this));
25856         }
25857     },
25858
25859     /**
25860      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25861      *   Will fail silently if the setUrl method has not been called.
25862      *   This does not activate the panel, just updates its content.
25863      */
25864     refresh : function(){
25865         if(this.refreshDelegate){
25866            this.loaded = false;
25867            this.refreshDelegate();
25868         }
25869     },
25870
25871     /** @private */
25872     _setLoaded : function(){
25873         this.loaded = true;
25874     },
25875
25876     /** @private */
25877     closeClick : function(e){
25878         var o = {};
25879         e.stopEvent();
25880         this.fireEvent("beforeclose", this, o);
25881         if(o.cancel !== true){
25882             this.tabPanel.removeTab(this.id);
25883         }
25884     },
25885     /**
25886      * The text displayed in the tooltip for the close icon.
25887      * @type String
25888      */
25889     closeText : "Close this tab"
25890 });
25891
25892 /** @private */
25893 Roo.TabPanel.prototype.createStrip = function(container){
25894     var strip = document.createElement("div");
25895     strip.className = "x-tabs-wrap";
25896     container.appendChild(strip);
25897     return strip;
25898 };
25899 /** @private */
25900 Roo.TabPanel.prototype.createStripList = function(strip){
25901     // div wrapper for retard IE
25902     // returns the "tr" element.
25903     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
25904         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
25905         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
25906     return strip.firstChild.firstChild.firstChild.firstChild;
25907 };
25908 /** @private */
25909 Roo.TabPanel.prototype.createBody = function(container){
25910     var body = document.createElement("div");
25911     Roo.id(body, "tab-body");
25912     Roo.fly(body).addClass("x-tabs-body");
25913     container.appendChild(body);
25914     return body;
25915 };
25916 /** @private */
25917 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25918     var body = Roo.getDom(id);
25919     if(!body){
25920         body = document.createElement("div");
25921         body.id = id;
25922     }
25923     Roo.fly(body).addClass("x-tabs-item-body");
25924     bodyEl.insertBefore(body, bodyEl.firstChild);
25925     return body;
25926 };
25927 /** @private */
25928 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25929     var td = document.createElement("td");
25930     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
25931     //stripEl.appendChild(td);
25932     if(closable){
25933         td.className = "x-tabs-closable";
25934         if(!this.closeTpl){
25935             this.closeTpl = new Roo.Template(
25936                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25937                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25938                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25939             );
25940         }
25941         var el = this.closeTpl.overwrite(td, {"text": text});
25942         var close = el.getElementsByTagName("div")[0];
25943         var inner = el.getElementsByTagName("em")[0];
25944         return {"el": el, "close": close, "inner": inner};
25945     } else {
25946         if(!this.tabTpl){
25947             this.tabTpl = new Roo.Template(
25948                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25949                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25950             );
25951         }
25952         var el = this.tabTpl.overwrite(td, {"text": text});
25953         var inner = el.getElementsByTagName("em")[0];
25954         return {"el": el, "inner": inner};
25955     }
25956 };/*
25957  * Based on:
25958  * Ext JS Library 1.1.1
25959  * Copyright(c) 2006-2007, Ext JS, LLC.
25960  *
25961  * Originally Released Under LGPL - original licence link has changed is not relivant.
25962  *
25963  * Fork - LGPL
25964  * <script type="text/javascript">
25965  */
25966
25967 /**
25968  * @class Roo.Button
25969  * @extends Roo.util.Observable
25970  * Simple Button class
25971  * @cfg {String} text The button text
25972  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25973  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25974  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25975  * @cfg {Object} scope The scope of the handler
25976  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25977  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25978  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25979  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25980  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25981  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25982    applies if enableToggle = true)
25983  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25984  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25985   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25986  * @constructor
25987  * Create a new button
25988  * @param {Object} config The config object
25989  */
25990 Roo.Button = function(renderTo, config)
25991 {
25992     if (!config) {
25993         config = renderTo;
25994         renderTo = config.renderTo || false;
25995     }
25996     
25997     Roo.apply(this, config);
25998     this.addEvents({
25999         /**
26000              * @event click
26001              * Fires when this button is clicked
26002              * @param {Button} this
26003              * @param {EventObject} e The click event
26004              */
26005             "click" : true,
26006         /**
26007              * @event toggle
26008              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26009              * @param {Button} this
26010              * @param {Boolean} pressed
26011              */
26012             "toggle" : true,
26013         /**
26014              * @event mouseover
26015              * Fires when the mouse hovers over the button
26016              * @param {Button} this
26017              * @param {Event} e The event object
26018              */
26019         'mouseover' : true,
26020         /**
26021              * @event mouseout
26022              * Fires when the mouse exits the button
26023              * @param {Button} this
26024              * @param {Event} e The event object
26025              */
26026         'mouseout': true,
26027          /**
26028              * @event render
26029              * Fires when the button is rendered
26030              * @param {Button} this
26031              */
26032         'render': true
26033     });
26034     if(this.menu){
26035         this.menu = Roo.menu.MenuMgr.get(this.menu);
26036     }
26037     // register listeners first!!  - so render can be captured..
26038     Roo.util.Observable.call(this);
26039     if(renderTo){
26040         this.render(renderTo);
26041     }
26042     
26043   
26044 };
26045
26046 Roo.extend(Roo.Button, Roo.util.Observable, {
26047     /**
26048      * 
26049      */
26050     
26051     /**
26052      * Read-only. True if this button is hidden
26053      * @type Boolean
26054      */
26055     hidden : false,
26056     /**
26057      * Read-only. True if this button is disabled
26058      * @type Boolean
26059      */
26060     disabled : false,
26061     /**
26062      * Read-only. True if this button is pressed (only if enableToggle = true)
26063      * @type Boolean
26064      */
26065     pressed : false,
26066
26067     /**
26068      * @cfg {Number} tabIndex 
26069      * The DOM tabIndex for this button (defaults to undefined)
26070      */
26071     tabIndex : undefined,
26072
26073     /**
26074      * @cfg {Boolean} enableToggle
26075      * True to enable pressed/not pressed toggling (defaults to false)
26076      */
26077     enableToggle: false,
26078     /**
26079      * @cfg {Mixed} menu
26080      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26081      */
26082     menu : undefined,
26083     /**
26084      * @cfg {String} menuAlign
26085      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26086      */
26087     menuAlign : "tl-bl?",
26088
26089     /**
26090      * @cfg {String} iconCls
26091      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26092      */
26093     iconCls : undefined,
26094     /**
26095      * @cfg {String} type
26096      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26097      */
26098     type : 'button',
26099
26100     // private
26101     menuClassTarget: 'tr',
26102
26103     /**
26104      * @cfg {String} clickEvent
26105      * The type of event to map to the button's event handler (defaults to 'click')
26106      */
26107     clickEvent : 'click',
26108
26109     /**
26110      * @cfg {Boolean} handleMouseEvents
26111      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26112      */
26113     handleMouseEvents : true,
26114
26115     /**
26116      * @cfg {String} tooltipType
26117      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26118      */
26119     tooltipType : 'qtip',
26120
26121     /**
26122      * @cfg {String} cls
26123      * A CSS class to apply to the button's main element.
26124      */
26125     
26126     /**
26127      * @cfg {Roo.Template} template (Optional)
26128      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26129      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26130      * require code modifications if required elements (e.g. a button) aren't present.
26131      */
26132
26133     // private
26134     render : function(renderTo){
26135         var btn;
26136         if(this.hideParent){
26137             this.parentEl = Roo.get(renderTo);
26138         }
26139         if(!this.dhconfig){
26140             if(!this.template){
26141                 if(!Roo.Button.buttonTemplate){
26142                     // hideous table template
26143                     Roo.Button.buttonTemplate = new Roo.Template(
26144                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26145                         '<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>',
26146                         "</tr></tbody></table>");
26147                 }
26148                 this.template = Roo.Button.buttonTemplate;
26149             }
26150             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26151             var btnEl = btn.child("button:first");
26152             btnEl.on('focus', this.onFocus, this);
26153             btnEl.on('blur', this.onBlur, this);
26154             if(this.cls){
26155                 btn.addClass(this.cls);
26156             }
26157             if(this.icon){
26158                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26159             }
26160             if(this.iconCls){
26161                 btnEl.addClass(this.iconCls);
26162                 if(!this.cls){
26163                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26164                 }
26165             }
26166             if(this.tabIndex !== undefined){
26167                 btnEl.dom.tabIndex = this.tabIndex;
26168             }
26169             if(this.tooltip){
26170                 if(typeof this.tooltip == 'object'){
26171                     Roo.QuickTips.tips(Roo.apply({
26172                           target: btnEl.id
26173                     }, this.tooltip));
26174                 } else {
26175                     btnEl.dom[this.tooltipType] = this.tooltip;
26176                 }
26177             }
26178         }else{
26179             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26180         }
26181         this.el = btn;
26182         if(this.id){
26183             this.el.dom.id = this.el.id = this.id;
26184         }
26185         if(this.menu){
26186             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26187             this.menu.on("show", this.onMenuShow, this);
26188             this.menu.on("hide", this.onMenuHide, this);
26189         }
26190         btn.addClass("x-btn");
26191         if(Roo.isIE && !Roo.isIE7){
26192             this.autoWidth.defer(1, this);
26193         }else{
26194             this.autoWidth();
26195         }
26196         if(this.handleMouseEvents){
26197             btn.on("mouseover", this.onMouseOver, this);
26198             btn.on("mouseout", this.onMouseOut, this);
26199             btn.on("mousedown", this.onMouseDown, this);
26200         }
26201         btn.on(this.clickEvent, this.onClick, this);
26202         //btn.on("mouseup", this.onMouseUp, this);
26203         if(this.hidden){
26204             this.hide();
26205         }
26206         if(this.disabled){
26207             this.disable();
26208         }
26209         Roo.ButtonToggleMgr.register(this);
26210         if(this.pressed){
26211             this.el.addClass("x-btn-pressed");
26212         }
26213         if(this.repeat){
26214             var repeater = new Roo.util.ClickRepeater(btn,
26215                 typeof this.repeat == "object" ? this.repeat : {}
26216             );
26217             repeater.on("click", this.onClick,  this);
26218         }
26219         
26220         this.fireEvent('render', this);
26221         
26222     },
26223     /**
26224      * Returns the button's underlying element
26225      * @return {Roo.Element} The element
26226      */
26227     getEl : function(){
26228         return this.el;  
26229     },
26230     
26231     /**
26232      * Destroys this Button and removes any listeners.
26233      */
26234     destroy : function(){
26235         Roo.ButtonToggleMgr.unregister(this);
26236         this.el.removeAllListeners();
26237         this.purgeListeners();
26238         this.el.remove();
26239     },
26240
26241     // private
26242     autoWidth : function(){
26243         if(this.el){
26244             this.el.setWidth("auto");
26245             if(Roo.isIE7 && Roo.isStrict){
26246                 var ib = this.el.child('button');
26247                 if(ib && ib.getWidth() > 20){
26248                     ib.clip();
26249                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26250                 }
26251             }
26252             if(this.minWidth){
26253                 if(this.hidden){
26254                     this.el.beginMeasure();
26255                 }
26256                 if(this.el.getWidth() < this.minWidth){
26257                     this.el.setWidth(this.minWidth);
26258                 }
26259                 if(this.hidden){
26260                     this.el.endMeasure();
26261                 }
26262             }
26263         }
26264     },
26265
26266     /**
26267      * Assigns this button's click handler
26268      * @param {Function} handler The function to call when the button is clicked
26269      * @param {Object} scope (optional) Scope for the function passed in
26270      */
26271     setHandler : function(handler, scope){
26272         this.handler = handler;
26273         this.scope = scope;  
26274     },
26275     
26276     /**
26277      * Sets this button's text
26278      * @param {String} text The button text
26279      */
26280     setText : function(text){
26281         this.text = text;
26282         if(this.el){
26283             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26284         }
26285         this.autoWidth();
26286     },
26287     
26288     /**
26289      * Gets the text for this button
26290      * @return {String} The button text
26291      */
26292     getText : function(){
26293         return this.text;  
26294     },
26295     
26296     /**
26297      * Show this button
26298      */
26299     show: function(){
26300         this.hidden = false;
26301         if(this.el){
26302             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26303         }
26304     },
26305     
26306     /**
26307      * Hide this button
26308      */
26309     hide: function(){
26310         this.hidden = true;
26311         if(this.el){
26312             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26313         }
26314     },
26315     
26316     /**
26317      * Convenience function for boolean show/hide
26318      * @param {Boolean} visible True to show, false to hide
26319      */
26320     setVisible: function(visible){
26321         if(visible) {
26322             this.show();
26323         }else{
26324             this.hide();
26325         }
26326     },
26327     
26328     /**
26329      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26330      * @param {Boolean} state (optional) Force a particular state
26331      */
26332     toggle : function(state){
26333         state = state === undefined ? !this.pressed : state;
26334         if(state != this.pressed){
26335             if(state){
26336                 this.el.addClass("x-btn-pressed");
26337                 this.pressed = true;
26338                 this.fireEvent("toggle", this, true);
26339             }else{
26340                 this.el.removeClass("x-btn-pressed");
26341                 this.pressed = false;
26342                 this.fireEvent("toggle", this, false);
26343             }
26344             if(this.toggleHandler){
26345                 this.toggleHandler.call(this.scope || this, this, state);
26346             }
26347         }
26348     },
26349     
26350     /**
26351      * Focus the button
26352      */
26353     focus : function(){
26354         this.el.child('button:first').focus();
26355     },
26356     
26357     /**
26358      * Disable this button
26359      */
26360     disable : function(){
26361         if(this.el){
26362             this.el.addClass("x-btn-disabled");
26363         }
26364         this.disabled = true;
26365     },
26366     
26367     /**
26368      * Enable this button
26369      */
26370     enable : function(){
26371         if(this.el){
26372             this.el.removeClass("x-btn-disabled");
26373         }
26374         this.disabled = false;
26375     },
26376
26377     /**
26378      * Convenience function for boolean enable/disable
26379      * @param {Boolean} enabled True to enable, false to disable
26380      */
26381     setDisabled : function(v){
26382         this[v !== true ? "enable" : "disable"]();
26383     },
26384
26385     // private
26386     onClick : function(e){
26387         if(e){
26388             e.preventDefault();
26389         }
26390         if(e.button != 0){
26391             return;
26392         }
26393         if(!this.disabled){
26394             if(this.enableToggle){
26395                 this.toggle();
26396             }
26397             if(this.menu && !this.menu.isVisible()){
26398                 this.menu.show(this.el, this.menuAlign);
26399             }
26400             this.fireEvent("click", this, e);
26401             if(this.handler){
26402                 this.el.removeClass("x-btn-over");
26403                 this.handler.call(this.scope || this, this, e);
26404             }
26405         }
26406     },
26407     // private
26408     onMouseOver : function(e){
26409         if(!this.disabled){
26410             this.el.addClass("x-btn-over");
26411             this.fireEvent('mouseover', this, e);
26412         }
26413     },
26414     // private
26415     onMouseOut : function(e){
26416         if(!e.within(this.el,  true)){
26417             this.el.removeClass("x-btn-over");
26418             this.fireEvent('mouseout', this, e);
26419         }
26420     },
26421     // private
26422     onFocus : function(e){
26423         if(!this.disabled){
26424             this.el.addClass("x-btn-focus");
26425         }
26426     },
26427     // private
26428     onBlur : function(e){
26429         this.el.removeClass("x-btn-focus");
26430     },
26431     // private
26432     onMouseDown : function(e){
26433         if(!this.disabled && e.button == 0){
26434             this.el.addClass("x-btn-click");
26435             Roo.get(document).on('mouseup', this.onMouseUp, this);
26436         }
26437     },
26438     // private
26439     onMouseUp : function(e){
26440         if(e.button == 0){
26441             this.el.removeClass("x-btn-click");
26442             Roo.get(document).un('mouseup', this.onMouseUp, this);
26443         }
26444     },
26445     // private
26446     onMenuShow : function(e){
26447         this.el.addClass("x-btn-menu-active");
26448     },
26449     // private
26450     onMenuHide : function(e){
26451         this.el.removeClass("x-btn-menu-active");
26452     }   
26453 });
26454
26455 // Private utility class used by Button
26456 Roo.ButtonToggleMgr = function(){
26457    var groups = {};
26458    
26459    function toggleGroup(btn, state){
26460        if(state){
26461            var g = groups[btn.toggleGroup];
26462            for(var i = 0, l = g.length; i < l; i++){
26463                if(g[i] != btn){
26464                    g[i].toggle(false);
26465                }
26466            }
26467        }
26468    }
26469    
26470    return {
26471        register : function(btn){
26472            if(!btn.toggleGroup){
26473                return;
26474            }
26475            var g = groups[btn.toggleGroup];
26476            if(!g){
26477                g = groups[btn.toggleGroup] = [];
26478            }
26479            g.push(btn);
26480            btn.on("toggle", toggleGroup);
26481        },
26482        
26483        unregister : function(btn){
26484            if(!btn.toggleGroup){
26485                return;
26486            }
26487            var g = groups[btn.toggleGroup];
26488            if(g){
26489                g.remove(btn);
26490                btn.un("toggle", toggleGroup);
26491            }
26492        }
26493    };
26494 }();/*
26495  * Based on:
26496  * Ext JS Library 1.1.1
26497  * Copyright(c) 2006-2007, Ext JS, LLC.
26498  *
26499  * Originally Released Under LGPL - original licence link has changed is not relivant.
26500  *
26501  * Fork - LGPL
26502  * <script type="text/javascript">
26503  */
26504  
26505 /**
26506  * @class Roo.SplitButton
26507  * @extends Roo.Button
26508  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26509  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26510  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26511  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26512  * @cfg {String} arrowTooltip The title attribute of the arrow
26513  * @constructor
26514  * Create a new menu button
26515  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26516  * @param {Object} config The config object
26517  */
26518 Roo.SplitButton = function(renderTo, config){
26519     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26520     /**
26521      * @event arrowclick
26522      * Fires when this button's arrow is clicked
26523      * @param {SplitButton} this
26524      * @param {EventObject} e The click event
26525      */
26526     this.addEvents({"arrowclick":true});
26527 };
26528
26529 Roo.extend(Roo.SplitButton, Roo.Button, {
26530     render : function(renderTo){
26531         // this is one sweet looking template!
26532         var tpl = new Roo.Template(
26533             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26534             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26535             '<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>',
26536             "</tbody></table></td><td>",
26537             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26538             '<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>',
26539             "</tbody></table></td></tr></table>"
26540         );
26541         var btn = tpl.append(renderTo, [this.text, this.type], true);
26542         var btnEl = btn.child("button");
26543         if(this.cls){
26544             btn.addClass(this.cls);
26545         }
26546         if(this.icon){
26547             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26548         }
26549         if(this.iconCls){
26550             btnEl.addClass(this.iconCls);
26551             if(!this.cls){
26552                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26553             }
26554         }
26555         this.el = btn;
26556         if(this.handleMouseEvents){
26557             btn.on("mouseover", this.onMouseOver, this);
26558             btn.on("mouseout", this.onMouseOut, this);
26559             btn.on("mousedown", this.onMouseDown, this);
26560             btn.on("mouseup", this.onMouseUp, this);
26561         }
26562         btn.on(this.clickEvent, this.onClick, this);
26563         if(this.tooltip){
26564             if(typeof this.tooltip == 'object'){
26565                 Roo.QuickTips.tips(Roo.apply({
26566                       target: btnEl.id
26567                 }, this.tooltip));
26568             } else {
26569                 btnEl.dom[this.tooltipType] = this.tooltip;
26570             }
26571         }
26572         if(this.arrowTooltip){
26573             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26574         }
26575         if(this.hidden){
26576             this.hide();
26577         }
26578         if(this.disabled){
26579             this.disable();
26580         }
26581         if(this.pressed){
26582             this.el.addClass("x-btn-pressed");
26583         }
26584         if(Roo.isIE && !Roo.isIE7){
26585             this.autoWidth.defer(1, this);
26586         }else{
26587             this.autoWidth();
26588         }
26589         if(this.menu){
26590             this.menu.on("show", this.onMenuShow, this);
26591             this.menu.on("hide", this.onMenuHide, this);
26592         }
26593         this.fireEvent('render', this);
26594     },
26595
26596     // private
26597     autoWidth : function(){
26598         if(this.el){
26599             var tbl = this.el.child("table:first");
26600             var tbl2 = this.el.child("table:last");
26601             this.el.setWidth("auto");
26602             tbl.setWidth("auto");
26603             if(Roo.isIE7 && Roo.isStrict){
26604                 var ib = this.el.child('button:first');
26605                 if(ib && ib.getWidth() > 20){
26606                     ib.clip();
26607                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26608                 }
26609             }
26610             if(this.minWidth){
26611                 if(this.hidden){
26612                     this.el.beginMeasure();
26613                 }
26614                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26615                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26616                 }
26617                 if(this.hidden){
26618                     this.el.endMeasure();
26619                 }
26620             }
26621             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26622         } 
26623     },
26624     /**
26625      * Sets this button's click handler
26626      * @param {Function} handler The function to call when the button is clicked
26627      * @param {Object} scope (optional) Scope for the function passed above
26628      */
26629     setHandler : function(handler, scope){
26630         this.handler = handler;
26631         this.scope = scope;  
26632     },
26633     
26634     /**
26635      * Sets this button's arrow click handler
26636      * @param {Function} handler The function to call when the arrow is clicked
26637      * @param {Object} scope (optional) Scope for the function passed above
26638      */
26639     setArrowHandler : function(handler, scope){
26640         this.arrowHandler = handler;
26641         this.scope = scope;  
26642     },
26643     
26644     /**
26645      * Focus the button
26646      */
26647     focus : function(){
26648         if(this.el){
26649             this.el.child("button:first").focus();
26650         }
26651     },
26652
26653     // private
26654     onClick : function(e){
26655         e.preventDefault();
26656         if(!this.disabled){
26657             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26658                 if(this.menu && !this.menu.isVisible()){
26659                     this.menu.show(this.el, this.menuAlign);
26660                 }
26661                 this.fireEvent("arrowclick", this, e);
26662                 if(this.arrowHandler){
26663                     this.arrowHandler.call(this.scope || this, this, e);
26664                 }
26665             }else{
26666                 this.fireEvent("click", this, e);
26667                 if(this.handler){
26668                     this.handler.call(this.scope || this, this, e);
26669                 }
26670             }
26671         }
26672     },
26673     // private
26674     onMouseDown : function(e){
26675         if(!this.disabled){
26676             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26677         }
26678     },
26679     // private
26680     onMouseUp : function(e){
26681         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26682     }   
26683 });
26684
26685
26686 // backwards compat
26687 Roo.MenuButton = Roo.SplitButton;/*
26688  * Based on:
26689  * Ext JS Library 1.1.1
26690  * Copyright(c) 2006-2007, Ext JS, LLC.
26691  *
26692  * Originally Released Under LGPL - original licence link has changed is not relivant.
26693  *
26694  * Fork - LGPL
26695  * <script type="text/javascript">
26696  */
26697
26698 /**
26699  * @class Roo.Toolbar
26700  * Basic Toolbar class.
26701  * @constructor
26702  * Creates a new Toolbar
26703  * @param {Object} config The config object
26704  */ 
26705 Roo.Toolbar = function(container, buttons, config)
26706 {
26707     /// old consturctor format still supported..
26708     if(container instanceof Array){ // omit the container for later rendering
26709         buttons = container;
26710         config = buttons;
26711         container = null;
26712     }
26713     if (typeof(container) == 'object' && container.xtype) {
26714         config = container;
26715         container = config.container;
26716         buttons = config.buttons; // not really - use items!!
26717     }
26718     var xitems = [];
26719     if (config && config.items) {
26720         xitems = config.items;
26721         delete config.items;
26722     }
26723     Roo.apply(this, config);
26724     this.buttons = buttons;
26725     
26726     if(container){
26727         this.render(container);
26728     }
26729     Roo.each(xitems, function(b) {
26730         this.add(b);
26731     }, this);
26732     
26733 };
26734
26735 Roo.Toolbar.prototype = {
26736     /**
26737      * @cfg {Roo.data.Store} items
26738      * array of button configs or elements to add
26739      */
26740     
26741     /**
26742      * @cfg {String/HTMLElement/Element} container
26743      * The id or element that will contain the toolbar
26744      */
26745     // private
26746     render : function(ct){
26747         this.el = Roo.get(ct);
26748         if(this.cls){
26749             this.el.addClass(this.cls);
26750         }
26751         // using a table allows for vertical alignment
26752         // 100% width is needed by Safari...
26753         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26754         this.tr = this.el.child("tr", true);
26755         var autoId = 0;
26756         this.items = new Roo.util.MixedCollection(false, function(o){
26757             return o.id || ("item" + (++autoId));
26758         });
26759         if(this.buttons){
26760             this.add.apply(this, this.buttons);
26761             delete this.buttons;
26762         }
26763     },
26764
26765     /**
26766      * Adds element(s) to the toolbar -- this function takes a variable number of 
26767      * arguments of mixed type and adds them to the toolbar.
26768      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26769      * <ul>
26770      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26771      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26772      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26773      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26774      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26775      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26776      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26777      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26778      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26779      * </ul>
26780      * @param {Mixed} arg2
26781      * @param {Mixed} etc.
26782      */
26783     add : function(){
26784         var a = arguments, l = a.length;
26785         for(var i = 0; i < l; i++){
26786             this._add(a[i]);
26787         }
26788     },
26789     // private..
26790     _add : function(el) {
26791         
26792         if (el.xtype) {
26793             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26794         }
26795         
26796         if (el.applyTo){ // some kind of form field
26797             return this.addField(el);
26798         } 
26799         if (el.render){ // some kind of Toolbar.Item
26800             return this.addItem(el);
26801         }
26802         if (typeof el == "string"){ // string
26803             if(el == "separator" || el == "-"){
26804                 return this.addSeparator();
26805             }
26806             if (el == " "){
26807                 return this.addSpacer();
26808             }
26809             if(el == "->"){
26810                 return this.addFill();
26811             }
26812             return this.addText(el);
26813             
26814         }
26815         if(el.tagName){ // element
26816             return this.addElement(el);
26817         }
26818         if(typeof el == "object"){ // must be button config?
26819             return this.addButton(el);
26820         }
26821         // and now what?!?!
26822         return false;
26823         
26824     },
26825     
26826     /**
26827      * Add an Xtype element
26828      * @param {Object} xtype Xtype Object
26829      * @return {Object} created Object
26830      */
26831     addxtype : function(e){
26832         return this.add(e);  
26833     },
26834     
26835     /**
26836      * Returns the Element for this toolbar.
26837      * @return {Roo.Element}
26838      */
26839     getEl : function(){
26840         return this.el;  
26841     },
26842     
26843     /**
26844      * Adds a separator
26845      * @return {Roo.Toolbar.Item} The separator item
26846      */
26847     addSeparator : function(){
26848         return this.addItem(new Roo.Toolbar.Separator());
26849     },
26850
26851     /**
26852      * Adds a spacer element
26853      * @return {Roo.Toolbar.Spacer} The spacer item
26854      */
26855     addSpacer : function(){
26856         return this.addItem(new Roo.Toolbar.Spacer());
26857     },
26858
26859     /**
26860      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26861      * @return {Roo.Toolbar.Fill} The fill item
26862      */
26863     addFill : function(){
26864         return this.addItem(new Roo.Toolbar.Fill());
26865     },
26866
26867     /**
26868      * Adds any standard HTML element to the toolbar
26869      * @param {String/HTMLElement/Element} el The element or id of the element to add
26870      * @return {Roo.Toolbar.Item} The element's item
26871      */
26872     addElement : function(el){
26873         return this.addItem(new Roo.Toolbar.Item(el));
26874     },
26875     /**
26876      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26877      * @type Roo.util.MixedCollection  
26878      */
26879     items : false,
26880      
26881     /**
26882      * Adds any Toolbar.Item or subclass
26883      * @param {Roo.Toolbar.Item} item
26884      * @return {Roo.Toolbar.Item} The item
26885      */
26886     addItem : function(item){
26887         var td = this.nextBlock();
26888         item.render(td);
26889         this.items.add(item);
26890         return item;
26891     },
26892     
26893     /**
26894      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26895      * @param {Object/Array} config A button config or array of configs
26896      * @return {Roo.Toolbar.Button/Array}
26897      */
26898     addButton : function(config){
26899         if(config instanceof Array){
26900             var buttons = [];
26901             for(var i = 0, len = config.length; i < len; i++) {
26902                 buttons.push(this.addButton(config[i]));
26903             }
26904             return buttons;
26905         }
26906         var b = config;
26907         if(!(config instanceof Roo.Toolbar.Button)){
26908             b = config.split ?
26909                 new Roo.Toolbar.SplitButton(config) :
26910                 new Roo.Toolbar.Button(config);
26911         }
26912         var td = this.nextBlock();
26913         b.render(td);
26914         this.items.add(b);
26915         return b;
26916     },
26917     
26918     /**
26919      * Adds text to the toolbar
26920      * @param {String} text The text to add
26921      * @return {Roo.Toolbar.Item} The element's item
26922      */
26923     addText : function(text){
26924         return this.addItem(new Roo.Toolbar.TextItem(text));
26925     },
26926     
26927     /**
26928      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26929      * @param {Number} index The index where the item is to be inserted
26930      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26931      * @return {Roo.Toolbar.Button/Item}
26932      */
26933     insertButton : function(index, item){
26934         if(item instanceof Array){
26935             var buttons = [];
26936             for(var i = 0, len = item.length; i < len; i++) {
26937                buttons.push(this.insertButton(index + i, item[i]));
26938             }
26939             return buttons;
26940         }
26941         if (!(item instanceof Roo.Toolbar.Button)){
26942            item = new Roo.Toolbar.Button(item);
26943         }
26944         var td = document.createElement("td");
26945         this.tr.insertBefore(td, this.tr.childNodes[index]);
26946         item.render(td);
26947         this.items.insert(index, item);
26948         return item;
26949     },
26950     
26951     /**
26952      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26953      * @param {Object} config
26954      * @return {Roo.Toolbar.Item} The element's item
26955      */
26956     addDom : function(config, returnEl){
26957         var td = this.nextBlock();
26958         Roo.DomHelper.overwrite(td, config);
26959         var ti = new Roo.Toolbar.Item(td.firstChild);
26960         ti.render(td);
26961         this.items.add(ti);
26962         return ti;
26963     },
26964
26965     /**
26966      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26967      * @type Roo.util.MixedCollection  
26968      */
26969     fields : false,
26970     
26971     /**
26972      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26973      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26974      * @param {Roo.form.Field} field
26975      * @return {Roo.ToolbarItem}
26976      */
26977      
26978       
26979     addField : function(field) {
26980         if (!this.fields) {
26981             var autoId = 0;
26982             this.fields = new Roo.util.MixedCollection(false, function(o){
26983                 return o.id || ("item" + (++autoId));
26984             });
26985
26986         }
26987         
26988         var td = this.nextBlock();
26989         field.render(td);
26990         var ti = new Roo.Toolbar.Item(td.firstChild);
26991         ti.render(td);
26992         this.items.add(ti);
26993         this.fields.add(field);
26994         return ti;
26995     },
26996     /**
26997      * Hide the toolbar
26998      * @method hide
26999      */
27000      
27001       
27002     hide : function()
27003     {
27004         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27005         this.el.child('div').hide();
27006     },
27007     /**
27008      * Show the toolbar
27009      * @method show
27010      */
27011     show : function()
27012     {
27013         this.el.child('div').show();
27014     },
27015       
27016     // private
27017     nextBlock : function(){
27018         var td = document.createElement("td");
27019         this.tr.appendChild(td);
27020         return td;
27021     },
27022
27023     // private
27024     destroy : function(){
27025         if(this.items){ // rendered?
27026             Roo.destroy.apply(Roo, this.items.items);
27027         }
27028         if(this.fields){ // rendered?
27029             Roo.destroy.apply(Roo, this.fields.items);
27030         }
27031         Roo.Element.uncache(this.el, this.tr);
27032     }
27033 };
27034
27035 /**
27036  * @class Roo.Toolbar.Item
27037  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27038  * @constructor
27039  * Creates a new Item
27040  * @param {HTMLElement} el 
27041  */
27042 Roo.Toolbar.Item = function(el){
27043     this.el = Roo.getDom(el);
27044     this.id = Roo.id(this.el);
27045     this.hidden = false;
27046 };
27047
27048 Roo.Toolbar.Item.prototype = {
27049     
27050     /**
27051      * Get this item's HTML Element
27052      * @return {HTMLElement}
27053      */
27054     getEl : function(){
27055        return this.el;  
27056     },
27057
27058     // private
27059     render : function(td){
27060         this.td = td;
27061         td.appendChild(this.el);
27062     },
27063     
27064     /**
27065      * Removes and destroys this item.
27066      */
27067     destroy : function(){
27068         this.td.parentNode.removeChild(this.td);
27069     },
27070     
27071     /**
27072      * Shows this item.
27073      */
27074     show: function(){
27075         this.hidden = false;
27076         this.td.style.display = "";
27077     },
27078     
27079     /**
27080      * Hides this item.
27081      */
27082     hide: function(){
27083         this.hidden = true;
27084         this.td.style.display = "none";
27085     },
27086     
27087     /**
27088      * Convenience function for boolean show/hide.
27089      * @param {Boolean} visible true to show/false to hide
27090      */
27091     setVisible: function(visible){
27092         if(visible) {
27093             this.show();
27094         }else{
27095             this.hide();
27096         }
27097     },
27098     
27099     /**
27100      * Try to focus this item.
27101      */
27102     focus : function(){
27103         Roo.fly(this.el).focus();
27104     },
27105     
27106     /**
27107      * Disables this item.
27108      */
27109     disable : function(){
27110         Roo.fly(this.td).addClass("x-item-disabled");
27111         this.disabled = true;
27112         this.el.disabled = true;
27113     },
27114     
27115     /**
27116      * Enables this item.
27117      */
27118     enable : function(){
27119         Roo.fly(this.td).removeClass("x-item-disabled");
27120         this.disabled = false;
27121         this.el.disabled = false;
27122     }
27123 };
27124
27125
27126 /**
27127  * @class Roo.Toolbar.Separator
27128  * @extends Roo.Toolbar.Item
27129  * A simple toolbar separator class
27130  * @constructor
27131  * Creates a new Separator
27132  */
27133 Roo.Toolbar.Separator = function(){
27134     var s = document.createElement("span");
27135     s.className = "ytb-sep";
27136     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27137 };
27138 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27139     enable:Roo.emptyFn,
27140     disable:Roo.emptyFn,
27141     focus:Roo.emptyFn
27142 });
27143
27144 /**
27145  * @class Roo.Toolbar.Spacer
27146  * @extends Roo.Toolbar.Item
27147  * A simple element that adds extra horizontal space to a toolbar.
27148  * @constructor
27149  * Creates a new Spacer
27150  */
27151 Roo.Toolbar.Spacer = function(){
27152     var s = document.createElement("div");
27153     s.className = "ytb-spacer";
27154     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27155 };
27156 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27157     enable:Roo.emptyFn,
27158     disable:Roo.emptyFn,
27159     focus:Roo.emptyFn
27160 });
27161
27162 /**
27163  * @class Roo.Toolbar.Fill
27164  * @extends Roo.Toolbar.Spacer
27165  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27166  * @constructor
27167  * Creates a new Spacer
27168  */
27169 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27170     // private
27171     render : function(td){
27172         td.style.width = '100%';
27173         Roo.Toolbar.Fill.superclass.render.call(this, td);
27174     }
27175 });
27176
27177 /**
27178  * @class Roo.Toolbar.TextItem
27179  * @extends Roo.Toolbar.Item
27180  * A simple class that renders text directly into a toolbar.
27181  * @constructor
27182  * Creates a new TextItem
27183  * @param {String} text
27184  */
27185 Roo.Toolbar.TextItem = function(text){
27186     if (typeof(text) == 'object') {
27187         text = text.text;
27188     }
27189     var s = document.createElement("span");
27190     s.className = "ytb-text";
27191     s.innerHTML = text;
27192     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27193 };
27194 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27195     enable:Roo.emptyFn,
27196     disable:Roo.emptyFn,
27197     focus:Roo.emptyFn
27198 });
27199
27200 /**
27201  * @class Roo.Toolbar.Button
27202  * @extends Roo.Button
27203  * A button that renders into a toolbar.
27204  * @constructor
27205  * Creates a new Button
27206  * @param {Object} config A standard {@link Roo.Button} config object
27207  */
27208 Roo.Toolbar.Button = function(config){
27209     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27210 };
27211 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27212     render : function(td){
27213         this.td = td;
27214         Roo.Toolbar.Button.superclass.render.call(this, td);
27215     },
27216     
27217     /**
27218      * Removes and destroys this button
27219      */
27220     destroy : function(){
27221         Roo.Toolbar.Button.superclass.destroy.call(this);
27222         this.td.parentNode.removeChild(this.td);
27223     },
27224     
27225     /**
27226      * Shows this button
27227      */
27228     show: function(){
27229         this.hidden = false;
27230         this.td.style.display = "";
27231     },
27232     
27233     /**
27234      * Hides this button
27235      */
27236     hide: function(){
27237         this.hidden = true;
27238         this.td.style.display = "none";
27239     },
27240
27241     /**
27242      * Disables this item
27243      */
27244     disable : function(){
27245         Roo.fly(this.td).addClass("x-item-disabled");
27246         this.disabled = true;
27247     },
27248
27249     /**
27250      * Enables this item
27251      */
27252     enable : function(){
27253         Roo.fly(this.td).removeClass("x-item-disabled");
27254         this.disabled = false;
27255     }
27256 });
27257 // backwards compat
27258 Roo.ToolbarButton = Roo.Toolbar.Button;
27259
27260 /**
27261  * @class Roo.Toolbar.SplitButton
27262  * @extends Roo.SplitButton
27263  * A menu button that renders into a toolbar.
27264  * @constructor
27265  * Creates a new SplitButton
27266  * @param {Object} config A standard {@link Roo.SplitButton} config object
27267  */
27268 Roo.Toolbar.SplitButton = function(config){
27269     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27270 };
27271 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27272     render : function(td){
27273         this.td = td;
27274         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27275     },
27276     
27277     /**
27278      * Removes and destroys this button
27279      */
27280     destroy : function(){
27281         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27282         this.td.parentNode.removeChild(this.td);
27283     },
27284     
27285     /**
27286      * Shows this button
27287      */
27288     show: function(){
27289         this.hidden = false;
27290         this.td.style.display = "";
27291     },
27292     
27293     /**
27294      * Hides this button
27295      */
27296     hide: function(){
27297         this.hidden = true;
27298         this.td.style.display = "none";
27299     }
27300 });
27301
27302 // backwards compat
27303 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27304  * Based on:
27305  * Ext JS Library 1.1.1
27306  * Copyright(c) 2006-2007, Ext JS, LLC.
27307  *
27308  * Originally Released Under LGPL - original licence link has changed is not relivant.
27309  *
27310  * Fork - LGPL
27311  * <script type="text/javascript">
27312  */
27313  
27314 /**
27315  * @class Roo.PagingToolbar
27316  * @extends Roo.Toolbar
27317  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27318  * @constructor
27319  * Create a new PagingToolbar
27320  * @param {Object} config The config object
27321  */
27322 Roo.PagingToolbar = function(el, ds, config)
27323 {
27324     // old args format still supported... - xtype is prefered..
27325     if (typeof(el) == 'object' && el.xtype) {
27326         // created from xtype...
27327         config = el;
27328         ds = el.dataSource;
27329         el = config.container;
27330     }
27331     var items = [];
27332     if (config.items) {
27333         items = config.items;
27334         config.items = [];
27335     }
27336     
27337     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27338     this.ds = ds;
27339     this.cursor = 0;
27340     this.renderButtons(this.el);
27341     this.bind(ds);
27342     
27343     // supprot items array.
27344    
27345     Roo.each(items, function(e) {
27346         this.add(Roo.factory(e));
27347     },this);
27348     
27349 };
27350
27351 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27352     /**
27353      * @cfg {Roo.data.Store} dataSource
27354      * The underlying data store providing the paged data
27355      */
27356     /**
27357      * @cfg {String/HTMLElement/Element} container
27358      * container The id or element that will contain the toolbar
27359      */
27360     /**
27361      * @cfg {Boolean} displayInfo
27362      * True to display the displayMsg (defaults to false)
27363      */
27364     /**
27365      * @cfg {Number} pageSize
27366      * The number of records to display per page (defaults to 20)
27367      */
27368     pageSize: 20,
27369     /**
27370      * @cfg {String} displayMsg
27371      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27372      */
27373     displayMsg : 'Displaying {0} - {1} of {2}',
27374     /**
27375      * @cfg {String} emptyMsg
27376      * The message to display when no records are found (defaults to "No data to display")
27377      */
27378     emptyMsg : 'No data to display',
27379     /**
27380      * Customizable piece of the default paging text (defaults to "Page")
27381      * @type String
27382      */
27383     beforePageText : "Page",
27384     /**
27385      * Customizable piece of the default paging text (defaults to "of %0")
27386      * @type String
27387      */
27388     afterPageText : "of {0}",
27389     /**
27390      * Customizable piece of the default paging text (defaults to "First Page")
27391      * @type String
27392      */
27393     firstText : "First Page",
27394     /**
27395      * Customizable piece of the default paging text (defaults to "Previous Page")
27396      * @type String
27397      */
27398     prevText : "Previous Page",
27399     /**
27400      * Customizable piece of the default paging text (defaults to "Next Page")
27401      * @type String
27402      */
27403     nextText : "Next Page",
27404     /**
27405      * Customizable piece of the default paging text (defaults to "Last Page")
27406      * @type String
27407      */
27408     lastText : "Last Page",
27409     /**
27410      * Customizable piece of the default paging text (defaults to "Refresh")
27411      * @type String
27412      */
27413     refreshText : "Refresh",
27414
27415     // private
27416     renderButtons : function(el){
27417         Roo.PagingToolbar.superclass.render.call(this, el);
27418         this.first = this.addButton({
27419             tooltip: this.firstText,
27420             cls: "x-btn-icon x-grid-page-first",
27421             disabled: true,
27422             handler: this.onClick.createDelegate(this, ["first"])
27423         });
27424         this.prev = this.addButton({
27425             tooltip: this.prevText,
27426             cls: "x-btn-icon x-grid-page-prev",
27427             disabled: true,
27428             handler: this.onClick.createDelegate(this, ["prev"])
27429         });
27430         //this.addSeparator();
27431         this.add(this.beforePageText);
27432         this.field = Roo.get(this.addDom({
27433            tag: "input",
27434            type: "text",
27435            size: "3",
27436            value: "1",
27437            cls: "x-grid-page-number"
27438         }).el);
27439         this.field.on("keydown", this.onPagingKeydown, this);
27440         this.field.on("focus", function(){this.dom.select();});
27441         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27442         this.field.setHeight(18);
27443         //this.addSeparator();
27444         this.next = this.addButton({
27445             tooltip: this.nextText,
27446             cls: "x-btn-icon x-grid-page-next",
27447             disabled: true,
27448             handler: this.onClick.createDelegate(this, ["next"])
27449         });
27450         this.last = this.addButton({
27451             tooltip: this.lastText,
27452             cls: "x-btn-icon x-grid-page-last",
27453             disabled: true,
27454             handler: this.onClick.createDelegate(this, ["last"])
27455         });
27456         //this.addSeparator();
27457         this.loading = this.addButton({
27458             tooltip: this.refreshText,
27459             cls: "x-btn-icon x-grid-loading",
27460             handler: this.onClick.createDelegate(this, ["refresh"])
27461         });
27462
27463         if(this.displayInfo){
27464             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27465         }
27466     },
27467
27468     // private
27469     updateInfo : function(){
27470         if(this.displayEl){
27471             var count = this.ds.getCount();
27472             var msg = count == 0 ?
27473                 this.emptyMsg :
27474                 String.format(
27475                     this.displayMsg,
27476                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27477                 );
27478             this.displayEl.update(msg);
27479         }
27480     },
27481
27482     // private
27483     onLoad : function(ds, r, o){
27484        this.cursor = o.params ? o.params.start : 0;
27485        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27486
27487        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27488        this.field.dom.value = ap;
27489        this.first.setDisabled(ap == 1);
27490        this.prev.setDisabled(ap == 1);
27491        this.next.setDisabled(ap == ps);
27492        this.last.setDisabled(ap == ps);
27493        this.loading.enable();
27494        this.updateInfo();
27495     },
27496
27497     // private
27498     getPageData : function(){
27499         var total = this.ds.getTotalCount();
27500         return {
27501             total : total,
27502             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27503             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27504         };
27505     },
27506
27507     // private
27508     onLoadError : function(){
27509         this.loading.enable();
27510     },
27511
27512     // private
27513     onPagingKeydown : function(e){
27514         var k = e.getKey();
27515         var d = this.getPageData();
27516         if(k == e.RETURN){
27517             var v = this.field.dom.value, pageNum;
27518             if(!v || isNaN(pageNum = parseInt(v, 10))){
27519                 this.field.dom.value = d.activePage;
27520                 return;
27521             }
27522             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27523             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27524             e.stopEvent();
27525         }
27526         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))
27527         {
27528           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27529           this.field.dom.value = pageNum;
27530           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27531           e.stopEvent();
27532         }
27533         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27534         {
27535           var v = this.field.dom.value, pageNum; 
27536           var increment = (e.shiftKey) ? 10 : 1;
27537           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27538             increment *= -1;
27539           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27540             this.field.dom.value = d.activePage;
27541             return;
27542           }
27543           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27544           {
27545             this.field.dom.value = parseInt(v, 10) + increment;
27546             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27547             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27548           }
27549           e.stopEvent();
27550         }
27551     },
27552
27553     // private
27554     beforeLoad : function(){
27555         if(this.loading){
27556             this.loading.disable();
27557         }
27558     },
27559
27560     // private
27561     onClick : function(which){
27562         var ds = this.ds;
27563         switch(which){
27564             case "first":
27565                 ds.load({params:{start: 0, limit: this.pageSize}});
27566             break;
27567             case "prev":
27568                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27569             break;
27570             case "next":
27571                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27572             break;
27573             case "last":
27574                 var total = ds.getTotalCount();
27575                 var extra = total % this.pageSize;
27576                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27577                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27578             break;
27579             case "refresh":
27580                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27581             break;
27582         }
27583     },
27584
27585     /**
27586      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27587      * @param {Roo.data.Store} store The data store to unbind
27588      */
27589     unbind : function(ds){
27590         ds.un("beforeload", this.beforeLoad, this);
27591         ds.un("load", this.onLoad, this);
27592         ds.un("loadexception", this.onLoadError, this);
27593         ds.un("remove", this.updateInfo, this);
27594         ds.un("add", this.updateInfo, this);
27595         this.ds = undefined;
27596     },
27597
27598     /**
27599      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27600      * @param {Roo.data.Store} store The data store to bind
27601      */
27602     bind : function(ds){
27603         ds.on("beforeload", this.beforeLoad, this);
27604         ds.on("load", this.onLoad, this);
27605         ds.on("loadexception", this.onLoadError, this);
27606         ds.on("remove", this.updateInfo, this);
27607         ds.on("add", this.updateInfo, this);
27608         this.ds = ds;
27609     }
27610 });/*
27611  * Based on:
27612  * Ext JS Library 1.1.1
27613  * Copyright(c) 2006-2007, Ext JS, LLC.
27614  *
27615  * Originally Released Under LGPL - original licence link has changed is not relivant.
27616  *
27617  * Fork - LGPL
27618  * <script type="text/javascript">
27619  */
27620
27621 /**
27622  * @class Roo.Resizable
27623  * @extends Roo.util.Observable
27624  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27625  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27626  * 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
27627  * the element will be wrapped for you automatically.</p>
27628  * <p>Here is the list of valid resize handles:</p>
27629  * <pre>
27630 Value   Description
27631 ------  -------------------
27632  'n'     north
27633  's'     south
27634  'e'     east
27635  'w'     west
27636  'nw'    northwest
27637  'sw'    southwest
27638  'se'    southeast
27639  'ne'    northeast
27640  'hd'    horizontal drag
27641  'all'   all
27642 </pre>
27643  * <p>Here's an example showing the creation of a typical Resizable:</p>
27644  * <pre><code>
27645 var resizer = new Roo.Resizable("element-id", {
27646     handles: 'all',
27647     minWidth: 200,
27648     minHeight: 100,
27649     maxWidth: 500,
27650     maxHeight: 400,
27651     pinned: true
27652 });
27653 resizer.on("resize", myHandler);
27654 </code></pre>
27655  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27656  * resizer.east.setDisplayed(false);</p>
27657  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27658  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27659  * resize operation's new size (defaults to [0, 0])
27660  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27661  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27662  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27663  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27664  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27665  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27666  * @cfg {Number} width The width of the element in pixels (defaults to null)
27667  * @cfg {Number} height The height of the element in pixels (defaults to null)
27668  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27669  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27670  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27671  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27672  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27673  * in favor of the handles config option (defaults to false)
27674  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27675  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27676  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27677  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27678  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27679  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27680  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27681  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27682  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27683  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27684  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27685  * @constructor
27686  * Create a new resizable component
27687  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27688  * @param {Object} config configuration options
27689   */
27690 Roo.Resizable = function(el, config)
27691 {
27692     this.el = Roo.get(el);
27693
27694     if(config && config.wrap){
27695         config.resizeChild = this.el;
27696         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27697         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27698         this.el.setStyle("overflow", "hidden");
27699         this.el.setPositioning(config.resizeChild.getPositioning());
27700         config.resizeChild.clearPositioning();
27701         if(!config.width || !config.height){
27702             var csize = config.resizeChild.getSize();
27703             this.el.setSize(csize.width, csize.height);
27704         }
27705         if(config.pinned && !config.adjustments){
27706             config.adjustments = "auto";
27707         }
27708     }
27709
27710     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27711     this.proxy.unselectable();
27712     this.proxy.enableDisplayMode('block');
27713
27714     Roo.apply(this, config);
27715
27716     if(this.pinned){
27717         this.disableTrackOver = true;
27718         this.el.addClass("x-resizable-pinned");
27719     }
27720     // if the element isn't positioned, make it relative
27721     var position = this.el.getStyle("position");
27722     if(position != "absolute" && position != "fixed"){
27723         this.el.setStyle("position", "relative");
27724     }
27725     if(!this.handles){ // no handles passed, must be legacy style
27726         this.handles = 's,e,se';
27727         if(this.multiDirectional){
27728             this.handles += ',n,w';
27729         }
27730     }
27731     if(this.handles == "all"){
27732         this.handles = "n s e w ne nw se sw";
27733     }
27734     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27735     var ps = Roo.Resizable.positions;
27736     for(var i = 0, len = hs.length; i < len; i++){
27737         if(hs[i] && ps[hs[i]]){
27738             var pos = ps[hs[i]];
27739             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27740         }
27741     }
27742     // legacy
27743     this.corner = this.southeast;
27744     
27745     // updateBox = the box can move..
27746     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27747         this.updateBox = true;
27748     }
27749
27750     this.activeHandle = null;
27751
27752     if(this.resizeChild){
27753         if(typeof this.resizeChild == "boolean"){
27754             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27755         }else{
27756             this.resizeChild = Roo.get(this.resizeChild, true);
27757         }
27758     }
27759     
27760     if(this.adjustments == "auto"){
27761         var rc = this.resizeChild;
27762         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27763         if(rc && (hw || hn)){
27764             rc.position("relative");
27765             rc.setLeft(hw ? hw.el.getWidth() : 0);
27766             rc.setTop(hn ? hn.el.getHeight() : 0);
27767         }
27768         this.adjustments = [
27769             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27770             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27771         ];
27772     }
27773
27774     if(this.draggable){
27775         this.dd = this.dynamic ?
27776             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27777         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27778     }
27779
27780     // public events
27781     this.addEvents({
27782         /**
27783          * @event beforeresize
27784          * Fired before resize is allowed. Set enabled to false to cancel resize.
27785          * @param {Roo.Resizable} this
27786          * @param {Roo.EventObject} e The mousedown event
27787          */
27788         "beforeresize" : true,
27789         /**
27790          * @event resize
27791          * Fired after a resize.
27792          * @param {Roo.Resizable} this
27793          * @param {Number} width The new width
27794          * @param {Number} height The new height
27795          * @param {Roo.EventObject} e The mouseup event
27796          */
27797         "resize" : true
27798     });
27799
27800     if(this.width !== null && this.height !== null){
27801         this.resizeTo(this.width, this.height);
27802     }else{
27803         this.updateChildSize();
27804     }
27805     if(Roo.isIE){
27806         this.el.dom.style.zoom = 1;
27807     }
27808     Roo.Resizable.superclass.constructor.call(this);
27809 };
27810
27811 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27812         resizeChild : false,
27813         adjustments : [0, 0],
27814         minWidth : 5,
27815         minHeight : 5,
27816         maxWidth : 10000,
27817         maxHeight : 10000,
27818         enabled : true,
27819         animate : false,
27820         duration : .35,
27821         dynamic : false,
27822         handles : false,
27823         multiDirectional : false,
27824         disableTrackOver : false,
27825         easing : 'easeOutStrong',
27826         widthIncrement : 0,
27827         heightIncrement : 0,
27828         pinned : false,
27829         width : null,
27830         height : null,
27831         preserveRatio : false,
27832         transparent: false,
27833         minX: 0,
27834         minY: 0,
27835         draggable: false,
27836
27837         /**
27838          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27839          */
27840         constrainTo: undefined,
27841         /**
27842          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27843          */
27844         resizeRegion: undefined,
27845
27846
27847     /**
27848      * Perform a manual resize
27849      * @param {Number} width
27850      * @param {Number} height
27851      */
27852     resizeTo : function(width, height){
27853         this.el.setSize(width, height);
27854         this.updateChildSize();
27855         this.fireEvent("resize", this, width, height, null);
27856     },
27857
27858     // private
27859     startSizing : function(e, handle){
27860         this.fireEvent("beforeresize", this, e);
27861         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27862
27863             if(!this.overlay){
27864                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27865                 this.overlay.unselectable();
27866                 this.overlay.enableDisplayMode("block");
27867                 this.overlay.on("mousemove", this.onMouseMove, this);
27868                 this.overlay.on("mouseup", this.onMouseUp, this);
27869             }
27870             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27871
27872             this.resizing = true;
27873             this.startBox = this.el.getBox();
27874             this.startPoint = e.getXY();
27875             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27876                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27877
27878             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27879             this.overlay.show();
27880
27881             if(this.constrainTo) {
27882                 var ct = Roo.get(this.constrainTo);
27883                 this.resizeRegion = ct.getRegion().adjust(
27884                     ct.getFrameWidth('t'),
27885                     ct.getFrameWidth('l'),
27886                     -ct.getFrameWidth('b'),
27887                     -ct.getFrameWidth('r')
27888                 );
27889             }
27890
27891             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27892             this.proxy.show();
27893             this.proxy.setBox(this.startBox);
27894             if(!this.dynamic){
27895                 this.proxy.setStyle('visibility', 'visible');
27896             }
27897         }
27898     },
27899
27900     // private
27901     onMouseDown : function(handle, e){
27902         if(this.enabled){
27903             e.stopEvent();
27904             this.activeHandle = handle;
27905             this.startSizing(e, handle);
27906         }
27907     },
27908
27909     // private
27910     onMouseUp : function(e){
27911         var size = this.resizeElement();
27912         this.resizing = false;
27913         this.handleOut();
27914         this.overlay.hide();
27915         this.proxy.hide();
27916         this.fireEvent("resize", this, size.width, size.height, e);
27917     },
27918
27919     // private
27920     updateChildSize : function(){
27921         if(this.resizeChild){
27922             var el = this.el;
27923             var child = this.resizeChild;
27924             var adj = this.adjustments;
27925             if(el.dom.offsetWidth){
27926                 var b = el.getSize(true);
27927                 child.setSize(b.width+adj[0], b.height+adj[1]);
27928             }
27929             // Second call here for IE
27930             // The first call enables instant resizing and
27931             // the second call corrects scroll bars if they
27932             // exist
27933             if(Roo.isIE){
27934                 setTimeout(function(){
27935                     if(el.dom.offsetWidth){
27936                         var b = el.getSize(true);
27937                         child.setSize(b.width+adj[0], b.height+adj[1]);
27938                     }
27939                 }, 10);
27940             }
27941         }
27942     },
27943
27944     // private
27945     snap : function(value, inc, min){
27946         if(!inc || !value) return value;
27947         var newValue = value;
27948         var m = value % inc;
27949         if(m > 0){
27950             if(m > (inc/2)){
27951                 newValue = value + (inc-m);
27952             }else{
27953                 newValue = value - m;
27954             }
27955         }
27956         return Math.max(min, newValue);
27957     },
27958
27959     // private
27960     resizeElement : function(){
27961         var box = this.proxy.getBox();
27962         if(this.updateBox){
27963             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27964         }else{
27965             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27966         }
27967         this.updateChildSize();
27968         if(!this.dynamic){
27969             this.proxy.hide();
27970         }
27971         return box;
27972     },
27973
27974     // private
27975     constrain : function(v, diff, m, mx){
27976         if(v - diff < m){
27977             diff = v - m;
27978         }else if(v - diff > mx){
27979             diff = mx - v;
27980         }
27981         return diff;
27982     },
27983
27984     // private
27985     onMouseMove : function(e){
27986         if(this.enabled){
27987             try{// try catch so if something goes wrong the user doesn't get hung
27988
27989             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27990                 return;
27991             }
27992
27993             //var curXY = this.startPoint;
27994             var curSize = this.curSize || this.startBox;
27995             var x = this.startBox.x, y = this.startBox.y;
27996             var ox = x, oy = y;
27997             var w = curSize.width, h = curSize.height;
27998             var ow = w, oh = h;
27999             var mw = this.minWidth, mh = this.minHeight;
28000             var mxw = this.maxWidth, mxh = this.maxHeight;
28001             var wi = this.widthIncrement;
28002             var hi = this.heightIncrement;
28003
28004             var eventXY = e.getXY();
28005             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28006             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28007
28008             var pos = this.activeHandle.position;
28009
28010             switch(pos){
28011                 case "east":
28012                     w += diffX;
28013                     w = Math.min(Math.max(mw, w), mxw);
28014                     break;
28015              
28016                 case "south":
28017                     h += diffY;
28018                     h = Math.min(Math.max(mh, h), mxh);
28019                     break;
28020                 case "southeast":
28021                     w += diffX;
28022                     h += diffY;
28023                     w = Math.min(Math.max(mw, w), mxw);
28024                     h = Math.min(Math.max(mh, h), mxh);
28025                     break;
28026                 case "north":
28027                     diffY = this.constrain(h, diffY, mh, mxh);
28028                     y += diffY;
28029                     h -= diffY;
28030                     break;
28031                 case "hdrag":
28032                     
28033                     if (wi) {
28034                         var adiffX = Math.abs(diffX);
28035                         var sub = (adiffX % wi); // how much 
28036                         if (sub > (wi/2)) { // far enough to snap
28037                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28038                         } else {
28039                             // remove difference.. 
28040                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28041                         }
28042                     }
28043                     x += diffX;
28044                     x = Math.max(this.minX, x);
28045                     break;
28046                 case "west":
28047                     diffX = this.constrain(w, diffX, mw, mxw);
28048                     x += diffX;
28049                     w -= diffX;
28050                     break;
28051                 case "northeast":
28052                     w += diffX;
28053                     w = Math.min(Math.max(mw, w), mxw);
28054                     diffY = this.constrain(h, diffY, mh, mxh);
28055                     y += diffY;
28056                     h -= diffY;
28057                     break;
28058                 case "northwest":
28059                     diffX = this.constrain(w, diffX, mw, mxw);
28060                     diffY = this.constrain(h, diffY, mh, mxh);
28061                     y += diffY;
28062                     h -= diffY;
28063                     x += diffX;
28064                     w -= diffX;
28065                     break;
28066                case "southwest":
28067                     diffX = this.constrain(w, diffX, mw, mxw);
28068                     h += diffY;
28069                     h = Math.min(Math.max(mh, h), mxh);
28070                     x += diffX;
28071                     w -= diffX;
28072                     break;
28073             }
28074
28075             var sw = this.snap(w, wi, mw);
28076             var sh = this.snap(h, hi, mh);
28077             if(sw != w || sh != h){
28078                 switch(pos){
28079                     case "northeast":
28080                         y -= sh - h;
28081                     break;
28082                     case "north":
28083                         y -= sh - h;
28084                         break;
28085                     case "southwest":
28086                         x -= sw - w;
28087                     break;
28088                     case "west":
28089                         x -= sw - w;
28090                         break;
28091                     case "northwest":
28092                         x -= sw - w;
28093                         y -= sh - h;
28094                     break;
28095                 }
28096                 w = sw;
28097                 h = sh;
28098             }
28099
28100             if(this.preserveRatio){
28101                 switch(pos){
28102                     case "southeast":
28103                     case "east":
28104                         h = oh * (w/ow);
28105                         h = Math.min(Math.max(mh, h), mxh);
28106                         w = ow * (h/oh);
28107                        break;
28108                     case "south":
28109                         w = ow * (h/oh);
28110                         w = Math.min(Math.max(mw, w), mxw);
28111                         h = oh * (w/ow);
28112                         break;
28113                     case "northeast":
28114                         w = ow * (h/oh);
28115                         w = Math.min(Math.max(mw, w), mxw);
28116                         h = oh * (w/ow);
28117                     break;
28118                     case "north":
28119                         var tw = w;
28120                         w = ow * (h/oh);
28121                         w = Math.min(Math.max(mw, w), mxw);
28122                         h = oh * (w/ow);
28123                         x += (tw - w) / 2;
28124                         break;
28125                     case "southwest":
28126                         h = oh * (w/ow);
28127                         h = Math.min(Math.max(mh, h), mxh);
28128                         var tw = w;
28129                         w = ow * (h/oh);
28130                         x += tw - w;
28131                         break;
28132                     case "west":
28133                         var th = h;
28134                         h = oh * (w/ow);
28135                         h = Math.min(Math.max(mh, h), mxh);
28136                         y += (th - h) / 2;
28137                         var tw = w;
28138                         w = ow * (h/oh);
28139                         x += tw - w;
28140                        break;
28141                     case "northwest":
28142                         var tw = w;
28143                         var th = h;
28144                         h = oh * (w/ow);
28145                         h = Math.min(Math.max(mh, h), mxh);
28146                         w = ow * (h/oh);
28147                         y += th - h;
28148                         x += tw - w;
28149                        break;
28150
28151                 }
28152             }
28153             if (pos == 'hdrag') {
28154                 w = ow;
28155             }
28156             this.proxy.setBounds(x, y, w, h);
28157             if(this.dynamic){
28158                 this.resizeElement();
28159             }
28160             }catch(e){}
28161         }
28162     },
28163
28164     // private
28165     handleOver : function(){
28166         if(this.enabled){
28167             this.el.addClass("x-resizable-over");
28168         }
28169     },
28170
28171     // private
28172     handleOut : function(){
28173         if(!this.resizing){
28174             this.el.removeClass("x-resizable-over");
28175         }
28176     },
28177
28178     /**
28179      * Returns the element this component is bound to.
28180      * @return {Roo.Element}
28181      */
28182     getEl : function(){
28183         return this.el;
28184     },
28185
28186     /**
28187      * Returns the resizeChild element (or null).
28188      * @return {Roo.Element}
28189      */
28190     getResizeChild : function(){
28191         return this.resizeChild;
28192     },
28193
28194     /**
28195      * Destroys this resizable. If the element was wrapped and
28196      * removeEl is not true then the element remains.
28197      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28198      */
28199     destroy : function(removeEl){
28200         this.proxy.remove();
28201         if(this.overlay){
28202             this.overlay.removeAllListeners();
28203             this.overlay.remove();
28204         }
28205         var ps = Roo.Resizable.positions;
28206         for(var k in ps){
28207             if(typeof ps[k] != "function" && this[ps[k]]){
28208                 var h = this[ps[k]];
28209                 h.el.removeAllListeners();
28210                 h.el.remove();
28211             }
28212         }
28213         if(removeEl){
28214             this.el.update("");
28215             this.el.remove();
28216         }
28217     }
28218 });
28219
28220 // private
28221 // hash to map config positions to true positions
28222 Roo.Resizable.positions = {
28223     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28224     hd: "hdrag"
28225 };
28226
28227 // private
28228 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28229     if(!this.tpl){
28230         // only initialize the template if resizable is used
28231         var tpl = Roo.DomHelper.createTemplate(
28232             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28233         );
28234         tpl.compile();
28235         Roo.Resizable.Handle.prototype.tpl = tpl;
28236     }
28237     this.position = pos;
28238     this.rz = rz;
28239     // show north drag fro topdra
28240     var handlepos = pos == 'hdrag' ? 'north' : pos;
28241     
28242     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28243     if (pos == 'hdrag') {
28244         this.el.setStyle('cursor', 'pointer');
28245     }
28246     this.el.unselectable();
28247     if(transparent){
28248         this.el.setOpacity(0);
28249     }
28250     this.el.on("mousedown", this.onMouseDown, this);
28251     if(!disableTrackOver){
28252         this.el.on("mouseover", this.onMouseOver, this);
28253         this.el.on("mouseout", this.onMouseOut, this);
28254     }
28255 };
28256
28257 // private
28258 Roo.Resizable.Handle.prototype = {
28259     afterResize : function(rz){
28260         // do nothing
28261     },
28262     // private
28263     onMouseDown : function(e){
28264         this.rz.onMouseDown(this, e);
28265     },
28266     // private
28267     onMouseOver : function(e){
28268         this.rz.handleOver(this, e);
28269     },
28270     // private
28271     onMouseOut : function(e){
28272         this.rz.handleOut(this, e);
28273     }
28274 };/*
28275  * Based on:
28276  * Ext JS Library 1.1.1
28277  * Copyright(c) 2006-2007, Ext JS, LLC.
28278  *
28279  * Originally Released Under LGPL - original licence link has changed is not relivant.
28280  *
28281  * Fork - LGPL
28282  * <script type="text/javascript">
28283  */
28284
28285 /**
28286  * @class Roo.Editor
28287  * @extends Roo.Component
28288  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28289  * @constructor
28290  * Create a new Editor
28291  * @param {Roo.form.Field} field The Field object (or descendant)
28292  * @param {Object} config The config object
28293  */
28294 Roo.Editor = function(field, config){
28295     Roo.Editor.superclass.constructor.call(this, config);
28296     this.field = field;
28297     this.addEvents({
28298         /**
28299              * @event beforestartedit
28300              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28301              * false from the handler of this event.
28302              * @param {Editor} this
28303              * @param {Roo.Element} boundEl The underlying element bound to this editor
28304              * @param {Mixed} value The field value being set
28305              */
28306         "beforestartedit" : true,
28307         /**
28308              * @event startedit
28309              * Fires when this editor is displayed
28310              * @param {Roo.Element} boundEl The underlying element bound to this editor
28311              * @param {Mixed} value The starting field value
28312              */
28313         "startedit" : true,
28314         /**
28315              * @event beforecomplete
28316              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28317              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28318              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28319              * event will not fire since no edit actually occurred.
28320              * @param {Editor} this
28321              * @param {Mixed} value The current field value
28322              * @param {Mixed} startValue The original field value
28323              */
28324         "beforecomplete" : true,
28325         /**
28326              * @event complete
28327              * Fires after editing is complete and any changed value has been written to the underlying field.
28328              * @param {Editor} this
28329              * @param {Mixed} value The current field value
28330              * @param {Mixed} startValue The original field value
28331              */
28332         "complete" : true,
28333         /**
28334          * @event specialkey
28335          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28336          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28337          * @param {Roo.form.Field} this
28338          * @param {Roo.EventObject} e The event object
28339          */
28340         "specialkey" : true
28341     });
28342 };
28343
28344 Roo.extend(Roo.Editor, Roo.Component, {
28345     /**
28346      * @cfg {Boolean/String} autosize
28347      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28348      * or "height" to adopt the height only (defaults to false)
28349      */
28350     /**
28351      * @cfg {Boolean} revertInvalid
28352      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28353      * validation fails (defaults to true)
28354      */
28355     /**
28356      * @cfg {Boolean} ignoreNoChange
28357      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28358      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28359      * will never be ignored.
28360      */
28361     /**
28362      * @cfg {Boolean} hideEl
28363      * False to keep the bound element visible while the editor is displayed (defaults to true)
28364      */
28365     /**
28366      * @cfg {Mixed} value
28367      * The data value of the underlying field (defaults to "")
28368      */
28369     value : "",
28370     /**
28371      * @cfg {String} alignment
28372      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28373      */
28374     alignment: "c-c?",
28375     /**
28376      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28377      * for bottom-right shadow (defaults to "frame")
28378      */
28379     shadow : "frame",
28380     /**
28381      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28382      */
28383     constrain : false,
28384     /**
28385      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28386      */
28387     completeOnEnter : false,
28388     /**
28389      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28390      */
28391     cancelOnEsc : false,
28392     /**
28393      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28394      */
28395     updateEl : false,
28396
28397     // private
28398     onRender : function(ct, position){
28399         this.el = new Roo.Layer({
28400             shadow: this.shadow,
28401             cls: "x-editor",
28402             parentEl : ct,
28403             shim : this.shim,
28404             shadowOffset:4,
28405             id: this.id,
28406             constrain: this.constrain
28407         });
28408         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28409         if(this.field.msgTarget != 'title'){
28410             this.field.msgTarget = 'qtip';
28411         }
28412         this.field.render(this.el);
28413         if(Roo.isGecko){
28414             this.field.el.dom.setAttribute('autocomplete', 'off');
28415         }
28416         this.field.on("specialkey", this.onSpecialKey, this);
28417         if(this.swallowKeys){
28418             this.field.el.swallowEvent(['keydown','keypress']);
28419         }
28420         this.field.show();
28421         this.field.on("blur", this.onBlur, this);
28422         if(this.field.grow){
28423             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28424         }
28425     },
28426
28427     onSpecialKey : function(field, e)
28428     {
28429         //Roo.log('editor onSpecialKey');
28430         if(this.completeOnEnter && e.getKey() == e.ENTER){
28431             e.stopEvent();
28432             this.completeEdit();
28433             return;
28434         }
28435         // do not fire special key otherwise it might hide close the editor...
28436         if(e.getKey() == e.ENTER){    
28437             return;
28438         }
28439         if(this.cancelOnEsc && e.getKey() == e.ESC){
28440             this.cancelEdit();
28441             return;
28442         } 
28443         this.fireEvent('specialkey', field, e);
28444     
28445     },
28446
28447     /**
28448      * Starts the editing process and shows the editor.
28449      * @param {String/HTMLElement/Element} el The element to edit
28450      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28451       * to the innerHTML of el.
28452      */
28453     startEdit : function(el, value){
28454         if(this.editing){
28455             this.completeEdit();
28456         }
28457         this.boundEl = Roo.get(el);
28458         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28459         if(!this.rendered){
28460             this.render(this.parentEl || document.body);
28461         }
28462         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28463             return;
28464         }
28465         this.startValue = v;
28466         this.field.setValue(v);
28467         if(this.autoSize){
28468             var sz = this.boundEl.getSize();
28469             switch(this.autoSize){
28470                 case "width":
28471                 this.setSize(sz.width,  "");
28472                 break;
28473                 case "height":
28474                 this.setSize("",  sz.height);
28475                 break;
28476                 default:
28477                 this.setSize(sz.width,  sz.height);
28478             }
28479         }
28480         this.el.alignTo(this.boundEl, this.alignment);
28481         this.editing = true;
28482         if(Roo.QuickTips){
28483             Roo.QuickTips.disable();
28484         }
28485         this.show();
28486     },
28487
28488     /**
28489      * Sets the height and width of this editor.
28490      * @param {Number} width The new width
28491      * @param {Number} height The new height
28492      */
28493     setSize : function(w, h){
28494         this.field.setSize(w, h);
28495         if(this.el){
28496             this.el.sync();
28497         }
28498     },
28499
28500     /**
28501      * Realigns the editor to the bound field based on the current alignment config value.
28502      */
28503     realign : function(){
28504         this.el.alignTo(this.boundEl, this.alignment);
28505     },
28506
28507     /**
28508      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28509      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28510      */
28511     completeEdit : function(remainVisible){
28512         if(!this.editing){
28513             return;
28514         }
28515         var v = this.getValue();
28516         if(this.revertInvalid !== false && !this.field.isValid()){
28517             v = this.startValue;
28518             this.cancelEdit(true);
28519         }
28520         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28521             this.editing = false;
28522             this.hide();
28523             return;
28524         }
28525         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28526             this.editing = false;
28527             if(this.updateEl && this.boundEl){
28528                 this.boundEl.update(v);
28529             }
28530             if(remainVisible !== true){
28531                 this.hide();
28532             }
28533             this.fireEvent("complete", this, v, this.startValue);
28534         }
28535     },
28536
28537     // private
28538     onShow : function(){
28539         this.el.show();
28540         if(this.hideEl !== false){
28541             this.boundEl.hide();
28542         }
28543         this.field.show();
28544         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28545             this.fixIEFocus = true;
28546             this.deferredFocus.defer(50, this);
28547         }else{
28548             this.field.focus();
28549         }
28550         this.fireEvent("startedit", this.boundEl, this.startValue);
28551     },
28552
28553     deferredFocus : function(){
28554         if(this.editing){
28555             this.field.focus();
28556         }
28557     },
28558
28559     /**
28560      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28561      * reverted to the original starting value.
28562      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28563      * cancel (defaults to false)
28564      */
28565     cancelEdit : function(remainVisible){
28566         if(this.editing){
28567             this.setValue(this.startValue);
28568             if(remainVisible !== true){
28569                 this.hide();
28570             }
28571         }
28572     },
28573
28574     // private
28575     onBlur : function(){
28576         if(this.allowBlur !== true && this.editing){
28577             this.completeEdit();
28578         }
28579     },
28580
28581     // private
28582     onHide : function(){
28583         if(this.editing){
28584             this.completeEdit();
28585             return;
28586         }
28587         this.field.blur();
28588         if(this.field.collapse){
28589             this.field.collapse();
28590         }
28591         this.el.hide();
28592         if(this.hideEl !== false){
28593             this.boundEl.show();
28594         }
28595         if(Roo.QuickTips){
28596             Roo.QuickTips.enable();
28597         }
28598     },
28599
28600     /**
28601      * Sets the data value of the editor
28602      * @param {Mixed} value Any valid value supported by the underlying field
28603      */
28604     setValue : function(v){
28605         this.field.setValue(v);
28606     },
28607
28608     /**
28609      * Gets the data value of the editor
28610      * @return {Mixed} The data value
28611      */
28612     getValue : function(){
28613         return this.field.getValue();
28614     }
28615 });/*
28616  * Based on:
28617  * Ext JS Library 1.1.1
28618  * Copyright(c) 2006-2007, Ext JS, LLC.
28619  *
28620  * Originally Released Under LGPL - original licence link has changed is not relivant.
28621  *
28622  * Fork - LGPL
28623  * <script type="text/javascript">
28624  */
28625  
28626 /**
28627  * @class Roo.BasicDialog
28628  * @extends Roo.util.Observable
28629  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28630  * <pre><code>
28631 var dlg = new Roo.BasicDialog("my-dlg", {
28632     height: 200,
28633     width: 300,
28634     minHeight: 100,
28635     minWidth: 150,
28636     modal: true,
28637     proxyDrag: true,
28638     shadow: true
28639 });
28640 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28641 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28642 dlg.addButton('Cancel', dlg.hide, dlg);
28643 dlg.show();
28644 </code></pre>
28645   <b>A Dialog should always be a direct child of the body element.</b>
28646  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28647  * @cfg {String} title Default text to display in the title bar (defaults to null)
28648  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28649  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28650  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28651  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28652  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28653  * (defaults to null with no animation)
28654  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28655  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28656  * property for valid values (defaults to 'all')
28657  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28658  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28659  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28660  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28661  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28662  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28663  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28664  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28665  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28666  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28667  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28668  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28669  * draggable = true (defaults to false)
28670  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28671  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28672  * shadow (defaults to false)
28673  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28674  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28675  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28676  * @cfg {Array} buttons Array of buttons
28677  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28678  * @constructor
28679  * Create a new BasicDialog.
28680  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28681  * @param {Object} config Configuration options
28682  */
28683 Roo.BasicDialog = function(el, config){
28684     this.el = Roo.get(el);
28685     var dh = Roo.DomHelper;
28686     if(!this.el && config && config.autoCreate){
28687         if(typeof config.autoCreate == "object"){
28688             if(!config.autoCreate.id){
28689                 config.autoCreate.id = el;
28690             }
28691             this.el = dh.append(document.body,
28692                         config.autoCreate, true);
28693         }else{
28694             this.el = dh.append(document.body,
28695                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28696         }
28697     }
28698     el = this.el;
28699     el.setDisplayed(true);
28700     el.hide = this.hideAction;
28701     this.id = el.id;
28702     el.addClass("x-dlg");
28703
28704     Roo.apply(this, config);
28705
28706     this.proxy = el.createProxy("x-dlg-proxy");
28707     this.proxy.hide = this.hideAction;
28708     this.proxy.setOpacity(.5);
28709     this.proxy.hide();
28710
28711     if(config.width){
28712         el.setWidth(config.width);
28713     }
28714     if(config.height){
28715         el.setHeight(config.height);
28716     }
28717     this.size = el.getSize();
28718     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28719         this.xy = [config.x,config.y];
28720     }else{
28721         this.xy = el.getCenterXY(true);
28722     }
28723     /** The header element @type Roo.Element */
28724     this.header = el.child("> .x-dlg-hd");
28725     /** The body element @type Roo.Element */
28726     this.body = el.child("> .x-dlg-bd");
28727     /** The footer element @type Roo.Element */
28728     this.footer = el.child("> .x-dlg-ft");
28729
28730     if(!this.header){
28731         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28732     }
28733     if(!this.body){
28734         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28735     }
28736
28737     this.header.unselectable();
28738     if(this.title){
28739         this.header.update(this.title);
28740     }
28741     // this element allows the dialog to be focused for keyboard event
28742     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28743     this.focusEl.swallowEvent("click", true);
28744
28745     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28746
28747     // wrap the body and footer for special rendering
28748     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28749     if(this.footer){
28750         this.bwrap.dom.appendChild(this.footer.dom);
28751     }
28752
28753     this.bg = this.el.createChild({
28754         tag: "div", cls:"x-dlg-bg",
28755         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28756     });
28757     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28758
28759
28760     if(this.autoScroll !== false && !this.autoTabs){
28761         this.body.setStyle("overflow", "auto");
28762     }
28763
28764     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28765
28766     if(this.closable !== false){
28767         this.el.addClass("x-dlg-closable");
28768         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28769         this.close.on("click", this.closeClick, this);
28770         this.close.addClassOnOver("x-dlg-close-over");
28771     }
28772     if(this.collapsible !== false){
28773         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28774         this.collapseBtn.on("click", this.collapseClick, this);
28775         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28776         this.header.on("dblclick", this.collapseClick, this);
28777     }
28778     if(this.resizable !== false){
28779         this.el.addClass("x-dlg-resizable");
28780         this.resizer = new Roo.Resizable(el, {
28781             minWidth: this.minWidth || 80,
28782             minHeight:this.minHeight || 80,
28783             handles: this.resizeHandles || "all",
28784             pinned: true
28785         });
28786         this.resizer.on("beforeresize", this.beforeResize, this);
28787         this.resizer.on("resize", this.onResize, this);
28788     }
28789     if(this.draggable !== false){
28790         el.addClass("x-dlg-draggable");
28791         if (!this.proxyDrag) {
28792             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28793         }
28794         else {
28795             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28796         }
28797         dd.setHandleElId(this.header.id);
28798         dd.endDrag = this.endMove.createDelegate(this);
28799         dd.startDrag = this.startMove.createDelegate(this);
28800         dd.onDrag = this.onDrag.createDelegate(this);
28801         dd.scroll = false;
28802         this.dd = dd;
28803     }
28804     if(this.modal){
28805         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28806         this.mask.enableDisplayMode("block");
28807         this.mask.hide();
28808         this.el.addClass("x-dlg-modal");
28809     }
28810     if(this.shadow){
28811         this.shadow = new Roo.Shadow({
28812             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28813             offset : this.shadowOffset
28814         });
28815     }else{
28816         this.shadowOffset = 0;
28817     }
28818     if(Roo.useShims && this.shim !== false){
28819         this.shim = this.el.createShim();
28820         this.shim.hide = this.hideAction;
28821         this.shim.hide();
28822     }else{
28823         this.shim = false;
28824     }
28825     if(this.autoTabs){
28826         this.initTabs();
28827     }
28828     if (this.buttons) { 
28829         var bts= this.buttons;
28830         this.buttons = [];
28831         Roo.each(bts, function(b) {
28832             this.addButton(b);
28833         }, this);
28834     }
28835     
28836     
28837     this.addEvents({
28838         /**
28839          * @event keydown
28840          * Fires when a key is pressed
28841          * @param {Roo.BasicDialog} this
28842          * @param {Roo.EventObject} e
28843          */
28844         "keydown" : true,
28845         /**
28846          * @event move
28847          * Fires when this dialog is moved by the user.
28848          * @param {Roo.BasicDialog} this
28849          * @param {Number} x The new page X
28850          * @param {Number} y The new page Y
28851          */
28852         "move" : true,
28853         /**
28854          * @event resize
28855          * Fires when this dialog is resized by the user.
28856          * @param {Roo.BasicDialog} this
28857          * @param {Number} width The new width
28858          * @param {Number} height The new height
28859          */
28860         "resize" : true,
28861         /**
28862          * @event beforehide
28863          * Fires before this dialog is hidden.
28864          * @param {Roo.BasicDialog} this
28865          */
28866         "beforehide" : true,
28867         /**
28868          * @event hide
28869          * Fires when this dialog is hidden.
28870          * @param {Roo.BasicDialog} this
28871          */
28872         "hide" : true,
28873         /**
28874          * @event beforeshow
28875          * Fires before this dialog is shown.
28876          * @param {Roo.BasicDialog} this
28877          */
28878         "beforeshow" : true,
28879         /**
28880          * @event show
28881          * Fires when this dialog is shown.
28882          * @param {Roo.BasicDialog} this
28883          */
28884         "show" : true
28885     });
28886     el.on("keydown", this.onKeyDown, this);
28887     el.on("mousedown", this.toFront, this);
28888     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28889     this.el.hide();
28890     Roo.DialogManager.register(this);
28891     Roo.BasicDialog.superclass.constructor.call(this);
28892 };
28893
28894 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28895     shadowOffset: Roo.isIE ? 6 : 5,
28896     minHeight: 80,
28897     minWidth: 200,
28898     minButtonWidth: 75,
28899     defaultButton: null,
28900     buttonAlign: "right",
28901     tabTag: 'div',
28902     firstShow: true,
28903
28904     /**
28905      * Sets the dialog title text
28906      * @param {String} text The title text to display
28907      * @return {Roo.BasicDialog} this
28908      */
28909     setTitle : function(text){
28910         this.header.update(text);
28911         return this;
28912     },
28913
28914     // private
28915     closeClick : function(){
28916         this.hide();
28917     },
28918
28919     // private
28920     collapseClick : function(){
28921         this[this.collapsed ? "expand" : "collapse"]();
28922     },
28923
28924     /**
28925      * Collapses the dialog to its minimized state (only the title bar is visible).
28926      * Equivalent to the user clicking the collapse dialog button.
28927      */
28928     collapse : function(){
28929         if(!this.collapsed){
28930             this.collapsed = true;
28931             this.el.addClass("x-dlg-collapsed");
28932             this.restoreHeight = this.el.getHeight();
28933             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28934         }
28935     },
28936
28937     /**
28938      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28939      * clicking the expand dialog button.
28940      */
28941     expand : function(){
28942         if(this.collapsed){
28943             this.collapsed = false;
28944             this.el.removeClass("x-dlg-collapsed");
28945             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28946         }
28947     },
28948
28949     /**
28950      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28951      * @return {Roo.TabPanel} The tabs component
28952      */
28953     initTabs : function(){
28954         var tabs = this.getTabs();
28955         while(tabs.getTab(0)){
28956             tabs.removeTab(0);
28957         }
28958         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28959             var dom = el.dom;
28960             tabs.addTab(Roo.id(dom), dom.title);
28961             dom.title = "";
28962         });
28963         tabs.activate(0);
28964         return tabs;
28965     },
28966
28967     // private
28968     beforeResize : function(){
28969         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28970     },
28971
28972     // private
28973     onResize : function(){
28974         this.refreshSize();
28975         this.syncBodyHeight();
28976         this.adjustAssets();
28977         this.focus();
28978         this.fireEvent("resize", this, this.size.width, this.size.height);
28979     },
28980
28981     // private
28982     onKeyDown : function(e){
28983         if(this.isVisible()){
28984             this.fireEvent("keydown", this, e);
28985         }
28986     },
28987
28988     /**
28989      * Resizes the dialog.
28990      * @param {Number} width
28991      * @param {Number} height
28992      * @return {Roo.BasicDialog} this
28993      */
28994     resizeTo : function(width, height){
28995         this.el.setSize(width, height);
28996         this.size = {width: width, height: height};
28997         this.syncBodyHeight();
28998         if(this.fixedcenter){
28999             this.center();
29000         }
29001         if(this.isVisible()){
29002             this.constrainXY();
29003             this.adjustAssets();
29004         }
29005         this.fireEvent("resize", this, width, height);
29006         return this;
29007     },
29008
29009
29010     /**
29011      * Resizes the dialog to fit the specified content size.
29012      * @param {Number} width
29013      * @param {Number} height
29014      * @return {Roo.BasicDialog} this
29015      */
29016     setContentSize : function(w, h){
29017         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29018         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29019         //if(!this.el.isBorderBox()){
29020             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29021             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29022         //}
29023         if(this.tabs){
29024             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29025             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29026         }
29027         this.resizeTo(w, h);
29028         return this;
29029     },
29030
29031     /**
29032      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29033      * executed in response to a particular key being pressed while the dialog is active.
29034      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29035      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29036      * @param {Function} fn The function to call
29037      * @param {Object} scope (optional) The scope of the function
29038      * @return {Roo.BasicDialog} this
29039      */
29040     addKeyListener : function(key, fn, scope){
29041         var keyCode, shift, ctrl, alt;
29042         if(typeof key == "object" && !(key instanceof Array)){
29043             keyCode = key["key"];
29044             shift = key["shift"];
29045             ctrl = key["ctrl"];
29046             alt = key["alt"];
29047         }else{
29048             keyCode = key;
29049         }
29050         var handler = function(dlg, e){
29051             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29052                 var k = e.getKey();
29053                 if(keyCode instanceof Array){
29054                     for(var i = 0, len = keyCode.length; i < len; i++){
29055                         if(keyCode[i] == k){
29056                           fn.call(scope || window, dlg, k, e);
29057                           return;
29058                         }
29059                     }
29060                 }else{
29061                     if(k == keyCode){
29062                         fn.call(scope || window, dlg, k, e);
29063                     }
29064                 }
29065             }
29066         };
29067         this.on("keydown", handler);
29068         return this;
29069     },
29070
29071     /**
29072      * Returns the TabPanel component (creates it if it doesn't exist).
29073      * Note: If you wish to simply check for the existence of tabs without creating them,
29074      * check for a null 'tabs' property.
29075      * @return {Roo.TabPanel} The tabs component
29076      */
29077     getTabs : function(){
29078         if(!this.tabs){
29079             this.el.addClass("x-dlg-auto-tabs");
29080             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29081             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29082         }
29083         return this.tabs;
29084     },
29085
29086     /**
29087      * Adds a button to the footer section of the dialog.
29088      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29089      * object or a valid Roo.DomHelper element config
29090      * @param {Function} handler The function called when the button is clicked
29091      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29092      * @return {Roo.Button} The new button
29093      */
29094     addButton : function(config, handler, scope){
29095         var dh = Roo.DomHelper;
29096         if(!this.footer){
29097             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29098         }
29099         if(!this.btnContainer){
29100             var tb = this.footer.createChild({
29101
29102                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29103                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29104             }, null, true);
29105             this.btnContainer = tb.firstChild.firstChild.firstChild;
29106         }
29107         var bconfig = {
29108             handler: handler,
29109             scope: scope,
29110             minWidth: this.minButtonWidth,
29111             hideParent:true
29112         };
29113         if(typeof config == "string"){
29114             bconfig.text = config;
29115         }else{
29116             if(config.tag){
29117                 bconfig.dhconfig = config;
29118             }else{
29119                 Roo.apply(bconfig, config);
29120             }
29121         }
29122         var fc = false;
29123         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29124             bconfig.position = Math.max(0, bconfig.position);
29125             fc = this.btnContainer.childNodes[bconfig.position];
29126         }
29127          
29128         var btn = new Roo.Button(
29129             fc ? 
29130                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29131                 : this.btnContainer.appendChild(document.createElement("td")),
29132             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29133             bconfig
29134         );
29135         this.syncBodyHeight();
29136         if(!this.buttons){
29137             /**
29138              * Array of all the buttons that have been added to this dialog via addButton
29139              * @type Array
29140              */
29141             this.buttons = [];
29142         }
29143         this.buttons.push(btn);
29144         return btn;
29145     },
29146
29147     /**
29148      * Sets the default button to be focused when the dialog is displayed.
29149      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29150      * @return {Roo.BasicDialog} this
29151      */
29152     setDefaultButton : function(btn){
29153         this.defaultButton = btn;
29154         return this;
29155     },
29156
29157     // private
29158     getHeaderFooterHeight : function(safe){
29159         var height = 0;
29160         if(this.header){
29161            height += this.header.getHeight();
29162         }
29163         if(this.footer){
29164            var fm = this.footer.getMargins();
29165             height += (this.footer.getHeight()+fm.top+fm.bottom);
29166         }
29167         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29168         height += this.centerBg.getPadding("tb");
29169         return height;
29170     },
29171
29172     // private
29173     syncBodyHeight : function(){
29174         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29175         var height = this.size.height - this.getHeaderFooterHeight(false);
29176         bd.setHeight(height-bd.getMargins("tb"));
29177         var hh = this.header.getHeight();
29178         var h = this.size.height-hh;
29179         cb.setHeight(h);
29180         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29181         bw.setHeight(h-cb.getPadding("tb"));
29182         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29183         bd.setWidth(bw.getWidth(true));
29184         if(this.tabs){
29185             this.tabs.syncHeight();
29186             if(Roo.isIE){
29187                 this.tabs.el.repaint();
29188             }
29189         }
29190     },
29191
29192     /**
29193      * Restores the previous state of the dialog if Roo.state is configured.
29194      * @return {Roo.BasicDialog} this
29195      */
29196     restoreState : function(){
29197         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29198         if(box && box.width){
29199             this.xy = [box.x, box.y];
29200             this.resizeTo(box.width, box.height);
29201         }
29202         return this;
29203     },
29204
29205     // private
29206     beforeShow : function(){
29207         this.expand();
29208         if(this.fixedcenter){
29209             this.xy = this.el.getCenterXY(true);
29210         }
29211         if(this.modal){
29212             Roo.get(document.body).addClass("x-body-masked");
29213             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29214             this.mask.show();
29215         }
29216         this.constrainXY();
29217     },
29218
29219     // private
29220     animShow : function(){
29221         var b = Roo.get(this.animateTarget).getBox();
29222         this.proxy.setSize(b.width, b.height);
29223         this.proxy.setLocation(b.x, b.y);
29224         this.proxy.show();
29225         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29226                     true, .35, this.showEl.createDelegate(this));
29227     },
29228
29229     /**
29230      * Shows the dialog.
29231      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29232      * @return {Roo.BasicDialog} this
29233      */
29234     show : function(animateTarget){
29235         if (this.fireEvent("beforeshow", this) === false){
29236             return;
29237         }
29238         if(this.syncHeightBeforeShow){
29239             this.syncBodyHeight();
29240         }else if(this.firstShow){
29241             this.firstShow = false;
29242             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29243         }
29244         this.animateTarget = animateTarget || this.animateTarget;
29245         if(!this.el.isVisible()){
29246             this.beforeShow();
29247             if(this.animateTarget && Roo.get(this.animateTarget)){
29248                 this.animShow();
29249             }else{
29250                 this.showEl();
29251             }
29252         }
29253         return this;
29254     },
29255
29256     // private
29257     showEl : function(){
29258         this.proxy.hide();
29259         this.el.setXY(this.xy);
29260         this.el.show();
29261         this.adjustAssets(true);
29262         this.toFront();
29263         this.focus();
29264         // IE peekaboo bug - fix found by Dave Fenwick
29265         if(Roo.isIE){
29266             this.el.repaint();
29267         }
29268         this.fireEvent("show", this);
29269     },
29270
29271     /**
29272      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29273      * dialog itself will receive focus.
29274      */
29275     focus : function(){
29276         if(this.defaultButton){
29277             this.defaultButton.focus();
29278         }else{
29279             this.focusEl.focus();
29280         }
29281     },
29282
29283     // private
29284     constrainXY : function(){
29285         if(this.constraintoviewport !== false){
29286             if(!this.viewSize){
29287                 if(this.container){
29288                     var s = this.container.getSize();
29289                     this.viewSize = [s.width, s.height];
29290                 }else{
29291                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29292                 }
29293             }
29294             var s = Roo.get(this.container||document).getScroll();
29295
29296             var x = this.xy[0], y = this.xy[1];
29297             var w = this.size.width, h = this.size.height;
29298             var vw = this.viewSize[0], vh = this.viewSize[1];
29299             // only move it if it needs it
29300             var moved = false;
29301             // first validate right/bottom
29302             if(x + w > vw+s.left){
29303                 x = vw - w;
29304                 moved = true;
29305             }
29306             if(y + h > vh+s.top){
29307                 y = vh - h;
29308                 moved = true;
29309             }
29310             // then make sure top/left isn't negative
29311             if(x < s.left){
29312                 x = s.left;
29313                 moved = true;
29314             }
29315             if(y < s.top){
29316                 y = s.top;
29317                 moved = true;
29318             }
29319             if(moved){
29320                 // cache xy
29321                 this.xy = [x, y];
29322                 if(this.isVisible()){
29323                     this.el.setLocation(x, y);
29324                     this.adjustAssets();
29325                 }
29326             }
29327         }
29328     },
29329
29330     // private
29331     onDrag : function(){
29332         if(!this.proxyDrag){
29333             this.xy = this.el.getXY();
29334             this.adjustAssets();
29335         }
29336     },
29337
29338     // private
29339     adjustAssets : function(doShow){
29340         var x = this.xy[0], y = this.xy[1];
29341         var w = this.size.width, h = this.size.height;
29342         if(doShow === true){
29343             if(this.shadow){
29344                 this.shadow.show(this.el);
29345             }
29346             if(this.shim){
29347                 this.shim.show();
29348             }
29349         }
29350         if(this.shadow && this.shadow.isVisible()){
29351             this.shadow.show(this.el);
29352         }
29353         if(this.shim && this.shim.isVisible()){
29354             this.shim.setBounds(x, y, w, h);
29355         }
29356     },
29357
29358     // private
29359     adjustViewport : function(w, h){
29360         if(!w || !h){
29361             w = Roo.lib.Dom.getViewWidth();
29362             h = Roo.lib.Dom.getViewHeight();
29363         }
29364         // cache the size
29365         this.viewSize = [w, h];
29366         if(this.modal && this.mask.isVisible()){
29367             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29368             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29369         }
29370         if(this.isVisible()){
29371             this.constrainXY();
29372         }
29373     },
29374
29375     /**
29376      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29377      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29378      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29379      */
29380     destroy : function(removeEl){
29381         if(this.isVisible()){
29382             this.animateTarget = null;
29383             this.hide();
29384         }
29385         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29386         if(this.tabs){
29387             this.tabs.destroy(removeEl);
29388         }
29389         Roo.destroy(
29390              this.shim,
29391              this.proxy,
29392              this.resizer,
29393              this.close,
29394              this.mask
29395         );
29396         if(this.dd){
29397             this.dd.unreg();
29398         }
29399         if(this.buttons){
29400            for(var i = 0, len = this.buttons.length; i < len; i++){
29401                this.buttons[i].destroy();
29402            }
29403         }
29404         this.el.removeAllListeners();
29405         if(removeEl === true){
29406             this.el.update("");
29407             this.el.remove();
29408         }
29409         Roo.DialogManager.unregister(this);
29410     },
29411
29412     // private
29413     startMove : function(){
29414         if(this.proxyDrag){
29415             this.proxy.show();
29416         }
29417         if(this.constraintoviewport !== false){
29418             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29419         }
29420     },
29421
29422     // private
29423     endMove : function(){
29424         if(!this.proxyDrag){
29425             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29426         }else{
29427             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29428             this.proxy.hide();
29429         }
29430         this.refreshSize();
29431         this.adjustAssets();
29432         this.focus();
29433         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29434     },
29435
29436     /**
29437      * Brings this dialog to the front of any other visible dialogs
29438      * @return {Roo.BasicDialog} this
29439      */
29440     toFront : function(){
29441         Roo.DialogManager.bringToFront(this);
29442         return this;
29443     },
29444
29445     /**
29446      * Sends this dialog to the back (under) of any other visible dialogs
29447      * @return {Roo.BasicDialog} this
29448      */
29449     toBack : function(){
29450         Roo.DialogManager.sendToBack(this);
29451         return this;
29452     },
29453
29454     /**
29455      * Centers this dialog in the viewport
29456      * @return {Roo.BasicDialog} this
29457      */
29458     center : function(){
29459         var xy = this.el.getCenterXY(true);
29460         this.moveTo(xy[0], xy[1]);
29461         return this;
29462     },
29463
29464     /**
29465      * Moves the dialog's top-left corner to the specified point
29466      * @param {Number} x
29467      * @param {Number} y
29468      * @return {Roo.BasicDialog} this
29469      */
29470     moveTo : function(x, y){
29471         this.xy = [x,y];
29472         if(this.isVisible()){
29473             this.el.setXY(this.xy);
29474             this.adjustAssets();
29475         }
29476         return this;
29477     },
29478
29479     /**
29480      * Aligns the dialog to the specified element
29481      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29482      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29483      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29484      * @return {Roo.BasicDialog} this
29485      */
29486     alignTo : function(element, position, offsets){
29487         this.xy = this.el.getAlignToXY(element, position, offsets);
29488         if(this.isVisible()){
29489             this.el.setXY(this.xy);
29490             this.adjustAssets();
29491         }
29492         return this;
29493     },
29494
29495     /**
29496      * Anchors an element to another element and realigns it when the window is resized.
29497      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29498      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29499      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29500      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29501      * is a number, it is used as the buffer delay (defaults to 50ms).
29502      * @return {Roo.BasicDialog} this
29503      */
29504     anchorTo : function(el, alignment, offsets, monitorScroll){
29505         var action = function(){
29506             this.alignTo(el, alignment, offsets);
29507         };
29508         Roo.EventManager.onWindowResize(action, this);
29509         var tm = typeof monitorScroll;
29510         if(tm != 'undefined'){
29511             Roo.EventManager.on(window, 'scroll', action, this,
29512                 {buffer: tm == 'number' ? monitorScroll : 50});
29513         }
29514         action.call(this);
29515         return this;
29516     },
29517
29518     /**
29519      * Returns true if the dialog is visible
29520      * @return {Boolean}
29521      */
29522     isVisible : function(){
29523         return this.el.isVisible();
29524     },
29525
29526     // private
29527     animHide : function(callback){
29528         var b = Roo.get(this.animateTarget).getBox();
29529         this.proxy.show();
29530         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29531         this.el.hide();
29532         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29533                     this.hideEl.createDelegate(this, [callback]));
29534     },
29535
29536     /**
29537      * Hides the dialog.
29538      * @param {Function} callback (optional) Function to call when the dialog is hidden
29539      * @return {Roo.BasicDialog} this
29540      */
29541     hide : function(callback){
29542         if (this.fireEvent("beforehide", this) === false){
29543             return;
29544         }
29545         if(this.shadow){
29546             this.shadow.hide();
29547         }
29548         if(this.shim) {
29549           this.shim.hide();
29550         }
29551         // sometimes animateTarget seems to get set.. causing problems...
29552         // this just double checks..
29553         if(this.animateTarget && Roo.get(this.animateTarget)) {
29554            this.animHide(callback);
29555         }else{
29556             this.el.hide();
29557             this.hideEl(callback);
29558         }
29559         return this;
29560     },
29561
29562     // private
29563     hideEl : function(callback){
29564         this.proxy.hide();
29565         if(this.modal){
29566             this.mask.hide();
29567             Roo.get(document.body).removeClass("x-body-masked");
29568         }
29569         this.fireEvent("hide", this);
29570         if(typeof callback == "function"){
29571             callback();
29572         }
29573     },
29574
29575     // private
29576     hideAction : function(){
29577         this.setLeft("-10000px");
29578         this.setTop("-10000px");
29579         this.setStyle("visibility", "hidden");
29580     },
29581
29582     // private
29583     refreshSize : function(){
29584         this.size = this.el.getSize();
29585         this.xy = this.el.getXY();
29586         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29587     },
29588
29589     // private
29590     // z-index is managed by the DialogManager and may be overwritten at any time
29591     setZIndex : function(index){
29592         if(this.modal){
29593             this.mask.setStyle("z-index", index);
29594         }
29595         if(this.shim){
29596             this.shim.setStyle("z-index", ++index);
29597         }
29598         if(this.shadow){
29599             this.shadow.setZIndex(++index);
29600         }
29601         this.el.setStyle("z-index", ++index);
29602         if(this.proxy){
29603             this.proxy.setStyle("z-index", ++index);
29604         }
29605         if(this.resizer){
29606             this.resizer.proxy.setStyle("z-index", ++index);
29607         }
29608
29609         this.lastZIndex = index;
29610     },
29611
29612     /**
29613      * Returns the element for this dialog
29614      * @return {Roo.Element} The underlying dialog Element
29615      */
29616     getEl : function(){
29617         return this.el;
29618     }
29619 });
29620
29621 /**
29622  * @class Roo.DialogManager
29623  * Provides global access to BasicDialogs that have been created and
29624  * support for z-indexing (layering) multiple open dialogs.
29625  */
29626 Roo.DialogManager = function(){
29627     var list = {};
29628     var accessList = [];
29629     var front = null;
29630
29631     // private
29632     var sortDialogs = function(d1, d2){
29633         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29634     };
29635
29636     // private
29637     var orderDialogs = function(){
29638         accessList.sort(sortDialogs);
29639         var seed = Roo.DialogManager.zseed;
29640         for(var i = 0, len = accessList.length; i < len; i++){
29641             var dlg = accessList[i];
29642             if(dlg){
29643                 dlg.setZIndex(seed + (i*10));
29644             }
29645         }
29646     };
29647
29648     return {
29649         /**
29650          * The starting z-index for BasicDialogs (defaults to 9000)
29651          * @type Number The z-index value
29652          */
29653         zseed : 9000,
29654
29655         // private
29656         register : function(dlg){
29657             list[dlg.id] = dlg;
29658             accessList.push(dlg);
29659         },
29660
29661         // private
29662         unregister : function(dlg){
29663             delete list[dlg.id];
29664             var i=0;
29665             var len=0;
29666             if(!accessList.indexOf){
29667                 for(  i = 0, len = accessList.length; i < len; i++){
29668                     if(accessList[i] == dlg){
29669                         accessList.splice(i, 1);
29670                         return;
29671                     }
29672                 }
29673             }else{
29674                  i = accessList.indexOf(dlg);
29675                 if(i != -1){
29676                     accessList.splice(i, 1);
29677                 }
29678             }
29679         },
29680
29681         /**
29682          * Gets a registered dialog by id
29683          * @param {String/Object} id The id of the dialog or a dialog
29684          * @return {Roo.BasicDialog} this
29685          */
29686         get : function(id){
29687             return typeof id == "object" ? id : list[id];
29688         },
29689
29690         /**
29691          * Brings the specified dialog to the front
29692          * @param {String/Object} dlg The id of the dialog or a dialog
29693          * @return {Roo.BasicDialog} this
29694          */
29695         bringToFront : function(dlg){
29696             dlg = this.get(dlg);
29697             if(dlg != front){
29698                 front = dlg;
29699                 dlg._lastAccess = new Date().getTime();
29700                 orderDialogs();
29701             }
29702             return dlg;
29703         },
29704
29705         /**
29706          * Sends the specified dialog to the back
29707          * @param {String/Object} dlg The id of the dialog or a dialog
29708          * @return {Roo.BasicDialog} this
29709          */
29710         sendToBack : function(dlg){
29711             dlg = this.get(dlg);
29712             dlg._lastAccess = -(new Date().getTime());
29713             orderDialogs();
29714             return dlg;
29715         },
29716
29717         /**
29718          * Hides all dialogs
29719          */
29720         hideAll : function(){
29721             for(var id in list){
29722                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29723                     list[id].hide();
29724                 }
29725             }
29726         }
29727     };
29728 }();
29729
29730 /**
29731  * @class Roo.LayoutDialog
29732  * @extends Roo.BasicDialog
29733  * Dialog which provides adjustments for working with a layout in a Dialog.
29734  * Add your necessary layout config options to the dialog's config.<br>
29735  * Example usage (including a nested layout):
29736  * <pre><code>
29737 if(!dialog){
29738     dialog = new Roo.LayoutDialog("download-dlg", {
29739         modal: true,
29740         width:600,
29741         height:450,
29742         shadow:true,
29743         minWidth:500,
29744         minHeight:350,
29745         autoTabs:true,
29746         proxyDrag:true,
29747         // layout config merges with the dialog config
29748         center:{
29749             tabPosition: "top",
29750             alwaysShowTabs: true
29751         }
29752     });
29753     dialog.addKeyListener(27, dialog.hide, dialog);
29754     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29755     dialog.addButton("Build It!", this.getDownload, this);
29756
29757     // we can even add nested layouts
29758     var innerLayout = new Roo.BorderLayout("dl-inner", {
29759         east: {
29760             initialSize: 200,
29761             autoScroll:true,
29762             split:true
29763         },
29764         center: {
29765             autoScroll:true
29766         }
29767     });
29768     innerLayout.beginUpdate();
29769     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29770     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29771     innerLayout.endUpdate(true);
29772
29773     var layout = dialog.getLayout();
29774     layout.beginUpdate();
29775     layout.add("center", new Roo.ContentPanel("standard-panel",
29776                         {title: "Download the Source", fitToFrame:true}));
29777     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29778                {title: "Build your own roo.js"}));
29779     layout.getRegion("center").showPanel(sp);
29780     layout.endUpdate();
29781 }
29782 </code></pre>
29783     * @constructor
29784     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29785     * @param {Object} config configuration options
29786   */
29787 Roo.LayoutDialog = function(el, cfg){
29788     
29789     var config=  cfg;
29790     if (typeof(cfg) == 'undefined') {
29791         config = Roo.apply({}, el);
29792         // not sure why we use documentElement here.. - it should always be body.
29793         // IE7 borks horribly if we use documentElement.
29794         // webkit also does not like documentElement - it creates a body element...
29795         el = Roo.get( document.body || document.documentElement ).createChild();
29796         //config.autoCreate = true;
29797     }
29798     
29799     
29800     config.autoTabs = false;
29801     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29802     this.body.setStyle({overflow:"hidden", position:"relative"});
29803     this.layout = new Roo.BorderLayout(this.body.dom, config);
29804     this.layout.monitorWindowResize = false;
29805     this.el.addClass("x-dlg-auto-layout");
29806     // fix case when center region overwrites center function
29807     this.center = Roo.BasicDialog.prototype.center;
29808     this.on("show", this.layout.layout, this.layout, true);
29809     if (config.items) {
29810         var xitems = config.items;
29811         delete config.items;
29812         Roo.each(xitems, this.addxtype, this);
29813     }
29814     
29815     
29816 };
29817 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29818     /**
29819      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29820      * @deprecated
29821      */
29822     endUpdate : function(){
29823         this.layout.endUpdate();
29824     },
29825
29826     /**
29827      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29828      *  @deprecated
29829      */
29830     beginUpdate : function(){
29831         this.layout.beginUpdate();
29832     },
29833
29834     /**
29835      * Get the BorderLayout for this dialog
29836      * @return {Roo.BorderLayout}
29837      */
29838     getLayout : function(){
29839         return this.layout;
29840     },
29841
29842     showEl : function(){
29843         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29844         if(Roo.isIE7){
29845             this.layout.layout();
29846         }
29847     },
29848
29849     // private
29850     // Use the syncHeightBeforeShow config option to control this automatically
29851     syncBodyHeight : function(){
29852         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29853         if(this.layout){this.layout.layout();}
29854     },
29855     
29856       /**
29857      * Add an xtype element (actually adds to the layout.)
29858      * @return {Object} xdata xtype object data.
29859      */
29860     
29861     addxtype : function(c) {
29862         return this.layout.addxtype(c);
29863     }
29864 });/*
29865  * Based on:
29866  * Ext JS Library 1.1.1
29867  * Copyright(c) 2006-2007, Ext JS, LLC.
29868  *
29869  * Originally Released Under LGPL - original licence link has changed is not relivant.
29870  *
29871  * Fork - LGPL
29872  * <script type="text/javascript">
29873  */
29874  
29875 /**
29876  * @class Roo.MessageBox
29877  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29878  * Example usage:
29879  *<pre><code>
29880 // Basic alert:
29881 Roo.Msg.alert('Status', 'Changes saved successfully.');
29882
29883 // Prompt for user data:
29884 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29885     if (btn == 'ok'){
29886         // process text value...
29887     }
29888 });
29889
29890 // Show a dialog using config options:
29891 Roo.Msg.show({
29892    title:'Save Changes?',
29893    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29894    buttons: Roo.Msg.YESNOCANCEL,
29895    fn: processResult,
29896    animEl: 'elId'
29897 });
29898 </code></pre>
29899  * @singleton
29900  */
29901 Roo.MessageBox = function(){
29902     var dlg, opt, mask, waitTimer;
29903     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29904     var buttons, activeTextEl, bwidth;
29905
29906     // private
29907     var handleButton = function(button){
29908         dlg.hide();
29909         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29910     };
29911
29912     // private
29913     var handleHide = function(){
29914         if(opt && opt.cls){
29915             dlg.el.removeClass(opt.cls);
29916         }
29917         if(waitTimer){
29918             Roo.TaskMgr.stop(waitTimer);
29919             waitTimer = null;
29920         }
29921     };
29922
29923     // private
29924     var updateButtons = function(b){
29925         var width = 0;
29926         if(!b){
29927             buttons["ok"].hide();
29928             buttons["cancel"].hide();
29929             buttons["yes"].hide();
29930             buttons["no"].hide();
29931             dlg.footer.dom.style.display = 'none';
29932             return width;
29933         }
29934         dlg.footer.dom.style.display = '';
29935         for(var k in buttons){
29936             if(typeof buttons[k] != "function"){
29937                 if(b[k]){
29938                     buttons[k].show();
29939                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29940                     width += buttons[k].el.getWidth()+15;
29941                 }else{
29942                     buttons[k].hide();
29943                 }
29944             }
29945         }
29946         return width;
29947     };
29948
29949     // private
29950     var handleEsc = function(d, k, e){
29951         if(opt && opt.closable !== false){
29952             dlg.hide();
29953         }
29954         if(e){
29955             e.stopEvent();
29956         }
29957     };
29958
29959     return {
29960         /**
29961          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29962          * @return {Roo.BasicDialog} The BasicDialog element
29963          */
29964         getDialog : function(){
29965            if(!dlg){
29966                 dlg = new Roo.BasicDialog("x-msg-box", {
29967                     autoCreate : true,
29968                     shadow: true,
29969                     draggable: true,
29970                     resizable:false,
29971                     constraintoviewport:false,
29972                     fixedcenter:true,
29973                     collapsible : false,
29974                     shim:true,
29975                     modal: true,
29976                     width:400, height:100,
29977                     buttonAlign:"center",
29978                     closeClick : function(){
29979                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29980                             handleButton("no");
29981                         }else{
29982                             handleButton("cancel");
29983                         }
29984                     }
29985                 });
29986                 dlg.on("hide", handleHide);
29987                 mask = dlg.mask;
29988                 dlg.addKeyListener(27, handleEsc);
29989                 buttons = {};
29990                 var bt = this.buttonText;
29991                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29992                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29993                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29994                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29995                 bodyEl = dlg.body.createChild({
29996
29997                     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>'
29998                 });
29999                 msgEl = bodyEl.dom.firstChild;
30000                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30001                 textboxEl.enableDisplayMode();
30002                 textboxEl.addKeyListener([10,13], function(){
30003                     if(dlg.isVisible() && opt && opt.buttons){
30004                         if(opt.buttons.ok){
30005                             handleButton("ok");
30006                         }else if(opt.buttons.yes){
30007                             handleButton("yes");
30008                         }
30009                     }
30010                 });
30011                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30012                 textareaEl.enableDisplayMode();
30013                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30014                 progressEl.enableDisplayMode();
30015                 var pf = progressEl.dom.firstChild;
30016                 if (pf) {
30017                     pp = Roo.get(pf.firstChild);
30018                     pp.setHeight(pf.offsetHeight);
30019                 }
30020                 
30021             }
30022             return dlg;
30023         },
30024
30025         /**
30026          * Updates the message box body text
30027          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30028          * the XHTML-compliant non-breaking space character '&amp;#160;')
30029          * @return {Roo.MessageBox} This message box
30030          */
30031         updateText : function(text){
30032             if(!dlg.isVisible() && !opt.width){
30033                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30034             }
30035             msgEl.innerHTML = text || '&#160;';
30036             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30037                         Math.max(opt.minWidth || this.minWidth, bwidth));
30038             if(opt.prompt){
30039                 activeTextEl.setWidth(w);
30040             }
30041             if(dlg.isVisible()){
30042                 dlg.fixedcenter = false;
30043             }
30044             dlg.setContentSize(w, bodyEl.getHeight());
30045             if(dlg.isVisible()){
30046                 dlg.fixedcenter = true;
30047             }
30048             return this;
30049         },
30050
30051         /**
30052          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30053          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30054          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30055          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30056          * @return {Roo.MessageBox} This message box
30057          */
30058         updateProgress : function(value, text){
30059             if(text){
30060                 this.updateText(text);
30061             }
30062             if (pp) { // weird bug on my firefox - for some reason this is not defined
30063                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30064             }
30065             return this;
30066         },        
30067
30068         /**
30069          * Returns true if the message box is currently displayed
30070          * @return {Boolean} True if the message box is visible, else false
30071          */
30072         isVisible : function(){
30073             return dlg && dlg.isVisible();  
30074         },
30075
30076         /**
30077          * Hides the message box if it is displayed
30078          */
30079         hide : function(){
30080             if(this.isVisible()){
30081                 dlg.hide();
30082             }  
30083         },
30084
30085         /**
30086          * Displays a new message box, or reinitializes an existing message box, based on the config options
30087          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30088          * The following config object properties are supported:
30089          * <pre>
30090 Property    Type             Description
30091 ----------  ---------------  ------------------------------------------------------------------------------------
30092 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30093                                    closes (defaults to undefined)
30094 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30095                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30096 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30097                                    progress and wait dialogs will ignore this property and always hide the
30098                                    close button as they can only be closed programmatically.
30099 cls               String           A custom CSS class to apply to the message box element
30100 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30101                                    displayed (defaults to 75)
30102 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30103                                    function will be btn (the name of the button that was clicked, if applicable,
30104                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30105                                    Progress and wait dialogs will ignore this option since they do not respond to
30106                                    user actions and can only be closed programmatically, so any required function
30107                                    should be called by the same code after it closes the dialog.
30108 icon              String           A CSS class that provides a background image to be used as an icon for
30109                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30110 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30111 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30112 modal             Boolean          False to allow user interaction with the page while the message box is
30113                                    displayed (defaults to true)
30114 msg               String           A string that will replace the existing message box body text (defaults
30115                                    to the XHTML-compliant non-breaking space character '&#160;')
30116 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30117 progress          Boolean          True to display a progress bar (defaults to false)
30118 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30119 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30120 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30121 title             String           The title text
30122 value             String           The string value to set into the active textbox element if displayed
30123 wait              Boolean          True to display a progress bar (defaults to false)
30124 width             Number           The width of the dialog in pixels
30125 </pre>
30126          *
30127          * Example usage:
30128          * <pre><code>
30129 Roo.Msg.show({
30130    title: 'Address',
30131    msg: 'Please enter your address:',
30132    width: 300,
30133    buttons: Roo.MessageBox.OKCANCEL,
30134    multiline: true,
30135    fn: saveAddress,
30136    animEl: 'addAddressBtn'
30137 });
30138 </code></pre>
30139          * @param {Object} config Configuration options
30140          * @return {Roo.MessageBox} This message box
30141          */
30142         show : function(options){
30143             if(this.isVisible()){
30144                 this.hide();
30145             }
30146             var d = this.getDialog();
30147             opt = options;
30148             d.setTitle(opt.title || "&#160;");
30149             d.close.setDisplayed(opt.closable !== false);
30150             activeTextEl = textboxEl;
30151             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30152             if(opt.prompt){
30153                 if(opt.multiline){
30154                     textboxEl.hide();
30155                     textareaEl.show();
30156                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30157                         opt.multiline : this.defaultTextHeight);
30158                     activeTextEl = textareaEl;
30159                 }else{
30160                     textboxEl.show();
30161                     textareaEl.hide();
30162                 }
30163             }else{
30164                 textboxEl.hide();
30165                 textareaEl.hide();
30166             }
30167             progressEl.setDisplayed(opt.progress === true);
30168             this.updateProgress(0);
30169             activeTextEl.dom.value = opt.value || "";
30170             if(opt.prompt){
30171                 dlg.setDefaultButton(activeTextEl);
30172             }else{
30173                 var bs = opt.buttons;
30174                 var db = null;
30175                 if(bs && bs.ok){
30176                     db = buttons["ok"];
30177                 }else if(bs && bs.yes){
30178                     db = buttons["yes"];
30179                 }
30180                 dlg.setDefaultButton(db);
30181             }
30182             bwidth = updateButtons(opt.buttons);
30183             this.updateText(opt.msg);
30184             if(opt.cls){
30185                 d.el.addClass(opt.cls);
30186             }
30187             d.proxyDrag = opt.proxyDrag === true;
30188             d.modal = opt.modal !== false;
30189             d.mask = opt.modal !== false ? mask : false;
30190             if(!d.isVisible()){
30191                 // force it to the end of the z-index stack so it gets a cursor in FF
30192                 document.body.appendChild(dlg.el.dom);
30193                 d.animateTarget = null;
30194                 d.show(options.animEl);
30195             }
30196             return this;
30197         },
30198
30199         /**
30200          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30201          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30202          * and closing the message box when the process is complete.
30203          * @param {String} title The title bar text
30204          * @param {String} msg The message box body text
30205          * @return {Roo.MessageBox} This message box
30206          */
30207         progress : function(title, msg){
30208             this.show({
30209                 title : title,
30210                 msg : msg,
30211                 buttons: false,
30212                 progress:true,
30213                 closable:false,
30214                 minWidth: this.minProgressWidth,
30215                 modal : true
30216             });
30217             return this;
30218         },
30219
30220         /**
30221          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30222          * If a callback function is passed it will be called after the user clicks the button, and the
30223          * id of the button that was clicked will be passed as the only parameter to the callback
30224          * (could also be the top-right close button).
30225          * @param {String} title The title bar text
30226          * @param {String} msg The message box body text
30227          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30228          * @param {Object} scope (optional) The scope of the callback function
30229          * @return {Roo.MessageBox} This message box
30230          */
30231         alert : function(title, msg, fn, scope){
30232             this.show({
30233                 title : title,
30234                 msg : msg,
30235                 buttons: this.OK,
30236                 fn: fn,
30237                 scope : scope,
30238                 modal : true
30239             });
30240             return this;
30241         },
30242
30243         /**
30244          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30245          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30246          * You are responsible for closing the message box when the process is complete.
30247          * @param {String} msg The message box body text
30248          * @param {String} title (optional) The title bar text
30249          * @return {Roo.MessageBox} This message box
30250          */
30251         wait : function(msg, title){
30252             this.show({
30253                 title : title,
30254                 msg : msg,
30255                 buttons: false,
30256                 closable:false,
30257                 progress:true,
30258                 modal:true,
30259                 width:300,
30260                 wait:true
30261             });
30262             waitTimer = Roo.TaskMgr.start({
30263                 run: function(i){
30264                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30265                 },
30266                 interval: 1000
30267             });
30268             return this;
30269         },
30270
30271         /**
30272          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30273          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30274          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30275          * @param {String} title The title bar text
30276          * @param {String} msg The message box body text
30277          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30278          * @param {Object} scope (optional) The scope of the callback function
30279          * @return {Roo.MessageBox} This message box
30280          */
30281         confirm : function(title, msg, fn, scope){
30282             this.show({
30283                 title : title,
30284                 msg : msg,
30285                 buttons: this.YESNO,
30286                 fn: fn,
30287                 scope : scope,
30288                 modal : true
30289             });
30290             return this;
30291         },
30292
30293         /**
30294          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30295          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30296          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30297          * (could also be the top-right close button) and the text that was entered will be passed as the two
30298          * parameters to the callback.
30299          * @param {String} title The title bar text
30300          * @param {String} msg The message box body text
30301          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30302          * @param {Object} scope (optional) The scope of the callback function
30303          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30304          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30305          * @return {Roo.MessageBox} This message box
30306          */
30307         prompt : function(title, msg, fn, scope, multiline){
30308             this.show({
30309                 title : title,
30310                 msg : msg,
30311                 buttons: this.OKCANCEL,
30312                 fn: fn,
30313                 minWidth:250,
30314                 scope : scope,
30315                 prompt:true,
30316                 multiline: multiline,
30317                 modal : true
30318             });
30319             return this;
30320         },
30321
30322         /**
30323          * Button config that displays a single OK button
30324          * @type Object
30325          */
30326         OK : {ok:true},
30327         /**
30328          * Button config that displays Yes and No buttons
30329          * @type Object
30330          */
30331         YESNO : {yes:true, no:true},
30332         /**
30333          * Button config that displays OK and Cancel buttons
30334          * @type Object
30335          */
30336         OKCANCEL : {ok:true, cancel:true},
30337         /**
30338          * Button config that displays Yes, No and Cancel buttons
30339          * @type Object
30340          */
30341         YESNOCANCEL : {yes:true, no:true, cancel:true},
30342
30343         /**
30344          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30345          * @type Number
30346          */
30347         defaultTextHeight : 75,
30348         /**
30349          * The maximum width in pixels of the message box (defaults to 600)
30350          * @type Number
30351          */
30352         maxWidth : 600,
30353         /**
30354          * The minimum width in pixels of the message box (defaults to 100)
30355          * @type Number
30356          */
30357         minWidth : 100,
30358         /**
30359          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30360          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30361          * @type Number
30362          */
30363         minProgressWidth : 250,
30364         /**
30365          * An object containing the default button text strings that can be overriden for localized language support.
30366          * Supported properties are: ok, cancel, yes and no.
30367          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30368          * @type Object
30369          */
30370         buttonText : {
30371             ok : "OK",
30372             cancel : "Cancel",
30373             yes : "Yes",
30374             no : "No"
30375         }
30376     };
30377 }();
30378
30379 /**
30380  * Shorthand for {@link Roo.MessageBox}
30381  */
30382 Roo.Msg = Roo.MessageBox;/*
30383  * Based on:
30384  * Ext JS Library 1.1.1
30385  * Copyright(c) 2006-2007, Ext JS, LLC.
30386  *
30387  * Originally Released Under LGPL - original licence link has changed is not relivant.
30388  *
30389  * Fork - LGPL
30390  * <script type="text/javascript">
30391  */
30392 /**
30393  * @class Roo.QuickTips
30394  * Provides attractive and customizable tooltips for any element.
30395  * @singleton
30396  */
30397 Roo.QuickTips = function(){
30398     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30399     var ce, bd, xy, dd;
30400     var visible = false, disabled = true, inited = false;
30401     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30402     
30403     var onOver = function(e){
30404         if(disabled){
30405             return;
30406         }
30407         var t = e.getTarget();
30408         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30409             return;
30410         }
30411         if(ce && t == ce.el){
30412             clearTimeout(hideProc);
30413             return;
30414         }
30415         if(t && tagEls[t.id]){
30416             tagEls[t.id].el = t;
30417             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30418             return;
30419         }
30420         var ttp, et = Roo.fly(t);
30421         var ns = cfg.namespace;
30422         if(tm.interceptTitles && t.title){
30423             ttp = t.title;
30424             t.qtip = ttp;
30425             t.removeAttribute("title");
30426             e.preventDefault();
30427         }else{
30428             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30429         }
30430         if(ttp){
30431             showProc = show.defer(tm.showDelay, tm, [{
30432                 el: t, 
30433                 text: ttp, 
30434                 width: et.getAttributeNS(ns, cfg.width),
30435                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30436                 title: et.getAttributeNS(ns, cfg.title),
30437                     cls: et.getAttributeNS(ns, cfg.cls)
30438             }]);
30439         }
30440     };
30441     
30442     var onOut = function(e){
30443         clearTimeout(showProc);
30444         var t = e.getTarget();
30445         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30446             hideProc = setTimeout(hide, tm.hideDelay);
30447         }
30448     };
30449     
30450     var onMove = function(e){
30451         if(disabled){
30452             return;
30453         }
30454         xy = e.getXY();
30455         xy[1] += 18;
30456         if(tm.trackMouse && ce){
30457             el.setXY(xy);
30458         }
30459     };
30460     
30461     var onDown = function(e){
30462         clearTimeout(showProc);
30463         clearTimeout(hideProc);
30464         if(!e.within(el)){
30465             if(tm.hideOnClick){
30466                 hide();
30467                 tm.disable();
30468                 tm.enable.defer(100, tm);
30469             }
30470         }
30471     };
30472     
30473     var getPad = function(){
30474         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30475     };
30476
30477     var show = function(o){
30478         if(disabled){
30479             return;
30480         }
30481         clearTimeout(dismissProc);
30482         ce = o;
30483         if(removeCls){ // in case manually hidden
30484             el.removeClass(removeCls);
30485             removeCls = null;
30486         }
30487         if(ce.cls){
30488             el.addClass(ce.cls);
30489             removeCls = ce.cls;
30490         }
30491         if(ce.title){
30492             tipTitle.update(ce.title);
30493             tipTitle.show();
30494         }else{
30495             tipTitle.update('');
30496             tipTitle.hide();
30497         }
30498         el.dom.style.width  = tm.maxWidth+'px';
30499         //tipBody.dom.style.width = '';
30500         tipBodyText.update(o.text);
30501         var p = getPad(), w = ce.width;
30502         if(!w){
30503             var td = tipBodyText.dom;
30504             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30505             if(aw > tm.maxWidth){
30506                 w = tm.maxWidth;
30507             }else if(aw < tm.minWidth){
30508                 w = tm.minWidth;
30509             }else{
30510                 w = aw;
30511             }
30512         }
30513         //tipBody.setWidth(w);
30514         el.setWidth(parseInt(w, 10) + p);
30515         if(ce.autoHide === false){
30516             close.setDisplayed(true);
30517             if(dd){
30518                 dd.unlock();
30519             }
30520         }else{
30521             close.setDisplayed(false);
30522             if(dd){
30523                 dd.lock();
30524             }
30525         }
30526         if(xy){
30527             el.avoidY = xy[1]-18;
30528             el.setXY(xy);
30529         }
30530         if(tm.animate){
30531             el.setOpacity(.1);
30532             el.setStyle("visibility", "visible");
30533             el.fadeIn({callback: afterShow});
30534         }else{
30535             afterShow();
30536         }
30537     };
30538     
30539     var afterShow = function(){
30540         if(ce){
30541             el.show();
30542             esc.enable();
30543             if(tm.autoDismiss && ce.autoHide !== false){
30544                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30545             }
30546         }
30547     };
30548     
30549     var hide = function(noanim){
30550         clearTimeout(dismissProc);
30551         clearTimeout(hideProc);
30552         ce = null;
30553         if(el.isVisible()){
30554             esc.disable();
30555             if(noanim !== true && tm.animate){
30556                 el.fadeOut({callback: afterHide});
30557             }else{
30558                 afterHide();
30559             } 
30560         }
30561     };
30562     
30563     var afterHide = function(){
30564         el.hide();
30565         if(removeCls){
30566             el.removeClass(removeCls);
30567             removeCls = null;
30568         }
30569     };
30570     
30571     return {
30572         /**
30573         * @cfg {Number} minWidth
30574         * The minimum width of the quick tip (defaults to 40)
30575         */
30576        minWidth : 40,
30577         /**
30578         * @cfg {Number} maxWidth
30579         * The maximum width of the quick tip (defaults to 300)
30580         */
30581        maxWidth : 300,
30582         /**
30583         * @cfg {Boolean} interceptTitles
30584         * True to automatically use the element's DOM title value if available (defaults to false)
30585         */
30586        interceptTitles : false,
30587         /**
30588         * @cfg {Boolean} trackMouse
30589         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30590         */
30591        trackMouse : false,
30592         /**
30593         * @cfg {Boolean} hideOnClick
30594         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30595         */
30596        hideOnClick : true,
30597         /**
30598         * @cfg {Number} showDelay
30599         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30600         */
30601        showDelay : 500,
30602         /**
30603         * @cfg {Number} hideDelay
30604         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30605         */
30606        hideDelay : 200,
30607         /**
30608         * @cfg {Boolean} autoHide
30609         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30610         * Used in conjunction with hideDelay.
30611         */
30612        autoHide : true,
30613         /**
30614         * @cfg {Boolean}
30615         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30616         * (defaults to true).  Used in conjunction with autoDismissDelay.
30617         */
30618        autoDismiss : true,
30619         /**
30620         * @cfg {Number}
30621         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30622         */
30623        autoDismissDelay : 5000,
30624        /**
30625         * @cfg {Boolean} animate
30626         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30627         */
30628        animate : false,
30629
30630        /**
30631         * @cfg {String} title
30632         * Title text to display (defaults to '').  This can be any valid HTML markup.
30633         */
30634         title: '',
30635        /**
30636         * @cfg {String} text
30637         * Body text to display (defaults to '').  This can be any valid HTML markup.
30638         */
30639         text : '',
30640        /**
30641         * @cfg {String} cls
30642         * A CSS class to apply to the base quick tip element (defaults to '').
30643         */
30644         cls : '',
30645        /**
30646         * @cfg {Number} width
30647         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30648         * minWidth or maxWidth.
30649         */
30650         width : null,
30651
30652     /**
30653      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30654      * or display QuickTips in a page.
30655      */
30656        init : function(){
30657           tm = Roo.QuickTips;
30658           cfg = tm.tagConfig;
30659           if(!inited){
30660               if(!Roo.isReady){ // allow calling of init() before onReady
30661                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30662                   return;
30663               }
30664               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30665               el.fxDefaults = {stopFx: true};
30666               // maximum custom styling
30667               //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>');
30668               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>');              
30669               tipTitle = el.child('h3');
30670               tipTitle.enableDisplayMode("block");
30671               tipBody = el.child('div.x-tip-bd');
30672               tipBodyText = el.child('div.x-tip-bd-inner');
30673               //bdLeft = el.child('div.x-tip-bd-left');
30674               //bdRight = el.child('div.x-tip-bd-right');
30675               close = el.child('div.x-tip-close');
30676               close.enableDisplayMode("block");
30677               close.on("click", hide);
30678               var d = Roo.get(document);
30679               d.on("mousedown", onDown);
30680               d.on("mouseover", onOver);
30681               d.on("mouseout", onOut);
30682               d.on("mousemove", onMove);
30683               esc = d.addKeyListener(27, hide);
30684               esc.disable();
30685               if(Roo.dd.DD){
30686                   dd = el.initDD("default", null, {
30687                       onDrag : function(){
30688                           el.sync();  
30689                       }
30690                   });
30691                   dd.setHandleElId(tipTitle.id);
30692                   dd.lock();
30693               }
30694               inited = true;
30695           }
30696           this.enable(); 
30697        },
30698
30699     /**
30700      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30701      * are supported:
30702      * <pre>
30703 Property    Type                   Description
30704 ----------  ---------------------  ------------------------------------------------------------------------
30705 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30706      * </ul>
30707      * @param {Object} config The config object
30708      */
30709        register : function(config){
30710            var cs = config instanceof Array ? config : arguments;
30711            for(var i = 0, len = cs.length; i < len; i++) {
30712                var c = cs[i];
30713                var target = c.target;
30714                if(target){
30715                    if(target instanceof Array){
30716                        for(var j = 0, jlen = target.length; j < jlen; j++){
30717                            tagEls[target[j]] = c;
30718                        }
30719                    }else{
30720                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30721                    }
30722                }
30723            }
30724        },
30725
30726     /**
30727      * Removes this quick tip from its element and destroys it.
30728      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30729      */
30730        unregister : function(el){
30731            delete tagEls[Roo.id(el)];
30732        },
30733
30734     /**
30735      * Enable this quick tip.
30736      */
30737        enable : function(){
30738            if(inited && disabled){
30739                locks.pop();
30740                if(locks.length < 1){
30741                    disabled = false;
30742                }
30743            }
30744        },
30745
30746     /**
30747      * Disable this quick tip.
30748      */
30749        disable : function(){
30750           disabled = true;
30751           clearTimeout(showProc);
30752           clearTimeout(hideProc);
30753           clearTimeout(dismissProc);
30754           if(ce){
30755               hide(true);
30756           }
30757           locks.push(1);
30758        },
30759
30760     /**
30761      * Returns true if the quick tip is enabled, else false.
30762      */
30763        isEnabled : function(){
30764             return !disabled;
30765        },
30766
30767         // private
30768        tagConfig : {
30769            namespace : "ext",
30770            attribute : "qtip",
30771            width : "width",
30772            target : "target",
30773            title : "qtitle",
30774            hide : "hide",
30775            cls : "qclass"
30776        }
30777    };
30778 }();
30779
30780 // backwards compat
30781 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30782  * Based on:
30783  * Ext JS Library 1.1.1
30784  * Copyright(c) 2006-2007, Ext JS, LLC.
30785  *
30786  * Originally Released Under LGPL - original licence link has changed is not relivant.
30787  *
30788  * Fork - LGPL
30789  * <script type="text/javascript">
30790  */
30791  
30792
30793 /**
30794  * @class Roo.tree.TreePanel
30795  * @extends Roo.data.Tree
30796
30797  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30798  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30799  * @cfg {Boolean} enableDD true to enable drag and drop
30800  * @cfg {Boolean} enableDrag true to enable just drag
30801  * @cfg {Boolean} enableDrop true to enable just drop
30802  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30803  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30804  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30805  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30806  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30807  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30808  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30809  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30810  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30811  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30812  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30813  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30814  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30815  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30816  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30817  * 
30818  * @constructor
30819  * @param {String/HTMLElement/Element} el The container element
30820  * @param {Object} config
30821  */
30822 Roo.tree.TreePanel = function(el, config){
30823     var root = false;
30824     var loader = false;
30825     if (config.root) {
30826         root = config.root;
30827         delete config.root;
30828     }
30829     if (config.loader) {
30830         loader = config.loader;
30831         delete config.loader;
30832     }
30833     
30834     Roo.apply(this, config);
30835     Roo.tree.TreePanel.superclass.constructor.call(this);
30836     this.el = Roo.get(el);
30837     this.el.addClass('x-tree');
30838     //console.log(root);
30839     if (root) {
30840         this.setRootNode( Roo.factory(root, Roo.tree));
30841     }
30842     if (loader) {
30843         this.loader = Roo.factory(loader, Roo.tree);
30844     }
30845    /**
30846     * Read-only. The id of the container element becomes this TreePanel's id.
30847     */
30848    this.id = this.el.id;
30849    this.addEvents({
30850         /**
30851         * @event beforeload
30852         * Fires before a node is loaded, return false to cancel
30853         * @param {Node} node The node being loaded
30854         */
30855         "beforeload" : true,
30856         /**
30857         * @event load
30858         * Fires when a node is loaded
30859         * @param {Node} node The node that was loaded
30860         */
30861         "load" : true,
30862         /**
30863         * @event textchange
30864         * Fires when the text for a node is changed
30865         * @param {Node} node The node
30866         * @param {String} text The new text
30867         * @param {String} oldText The old text
30868         */
30869         "textchange" : true,
30870         /**
30871         * @event beforeexpand
30872         * Fires before a node is expanded, return false to cancel.
30873         * @param {Node} node The node
30874         * @param {Boolean} deep
30875         * @param {Boolean} anim
30876         */
30877         "beforeexpand" : true,
30878         /**
30879         * @event beforecollapse
30880         * Fires before a node is collapsed, return false to cancel.
30881         * @param {Node} node The node
30882         * @param {Boolean} deep
30883         * @param {Boolean} anim
30884         */
30885         "beforecollapse" : true,
30886         /**
30887         * @event expand
30888         * Fires when a node is expanded
30889         * @param {Node} node The node
30890         */
30891         "expand" : true,
30892         /**
30893         * @event disabledchange
30894         * Fires when the disabled status of a node changes
30895         * @param {Node} node The node
30896         * @param {Boolean} disabled
30897         */
30898         "disabledchange" : true,
30899         /**
30900         * @event collapse
30901         * Fires when a node is collapsed
30902         * @param {Node} node The node
30903         */
30904         "collapse" : true,
30905         /**
30906         * @event beforeclick
30907         * Fires before click processing on a node. Return false to cancel the default action.
30908         * @param {Node} node The node
30909         * @param {Roo.EventObject} e The event object
30910         */
30911         "beforeclick":true,
30912         /**
30913         * @event checkchange
30914         * Fires when a node with a checkbox's checked property changes
30915         * @param {Node} this This node
30916         * @param {Boolean} checked
30917         */
30918         "checkchange":true,
30919         /**
30920         * @event click
30921         * Fires when a node is clicked
30922         * @param {Node} node The node
30923         * @param {Roo.EventObject} e The event object
30924         */
30925         "click":true,
30926         /**
30927         * @event dblclick
30928         * Fires when a node is double clicked
30929         * @param {Node} node The node
30930         * @param {Roo.EventObject} e The event object
30931         */
30932         "dblclick":true,
30933         /**
30934         * @event contextmenu
30935         * Fires when a node is right clicked
30936         * @param {Node} node The node
30937         * @param {Roo.EventObject} e The event object
30938         */
30939         "contextmenu":true,
30940         /**
30941         * @event beforechildrenrendered
30942         * Fires right before the child nodes for a node are rendered
30943         * @param {Node} node The node
30944         */
30945         "beforechildrenrendered":true,
30946        /**
30947              * @event startdrag
30948              * Fires when a node starts being dragged
30949              * @param {Roo.tree.TreePanel} this
30950              * @param {Roo.tree.TreeNode} node
30951              * @param {event} e The raw browser event
30952              */ 
30953             "startdrag" : true,
30954             /**
30955              * @event enddrag
30956              * Fires when a drag operation is complete
30957              * @param {Roo.tree.TreePanel} this
30958              * @param {Roo.tree.TreeNode} node
30959              * @param {event} e The raw browser event
30960              */
30961             "enddrag" : true,
30962             /**
30963              * @event dragdrop
30964              * Fires when a dragged node is dropped on a valid DD target
30965              * @param {Roo.tree.TreePanel} this
30966              * @param {Roo.tree.TreeNode} node
30967              * @param {DD} dd The dd it was dropped on
30968              * @param {event} e The raw browser event
30969              */
30970             "dragdrop" : true,
30971             /**
30972              * @event beforenodedrop
30973              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30974              * passed to handlers has the following properties:<br />
30975              * <ul style="padding:5px;padding-left:16px;">
30976              * <li>tree - The TreePanel</li>
30977              * <li>target - The node being targeted for the drop</li>
30978              * <li>data - The drag data from the drag source</li>
30979              * <li>point - The point of the drop - append, above or below</li>
30980              * <li>source - The drag source</li>
30981              * <li>rawEvent - Raw mouse event</li>
30982              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30983              * to be inserted by setting them on this object.</li>
30984              * <li>cancel - Set this to true to cancel the drop.</li>
30985              * </ul>
30986              * @param {Object} dropEvent
30987              */
30988             "beforenodedrop" : true,
30989             /**
30990              * @event nodedrop
30991              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30992              * passed to handlers has the following properties:<br />
30993              * <ul style="padding:5px;padding-left:16px;">
30994              * <li>tree - The TreePanel</li>
30995              * <li>target - The node being targeted for the drop</li>
30996              * <li>data - The drag data from the drag source</li>
30997              * <li>point - The point of the drop - append, above or below</li>
30998              * <li>source - The drag source</li>
30999              * <li>rawEvent - Raw mouse event</li>
31000              * <li>dropNode - Dropped node(s).</li>
31001              * </ul>
31002              * @param {Object} dropEvent
31003              */
31004             "nodedrop" : true,
31005              /**
31006              * @event nodedragover
31007              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31008              * passed to handlers has the following properties:<br />
31009              * <ul style="padding:5px;padding-left:16px;">
31010              * <li>tree - The TreePanel</li>
31011              * <li>target - The node being targeted for the drop</li>
31012              * <li>data - The drag data from the drag source</li>
31013              * <li>point - The point of the drop - append, above or below</li>
31014              * <li>source - The drag source</li>
31015              * <li>rawEvent - Raw mouse event</li>
31016              * <li>dropNode - Drop node(s) provided by the source.</li>
31017              * <li>cancel - Set this to true to signal drop not allowed.</li>
31018              * </ul>
31019              * @param {Object} dragOverEvent
31020              */
31021             "nodedragover" : true
31022         
31023    });
31024    if(this.singleExpand){
31025        this.on("beforeexpand", this.restrictExpand, this);
31026    }
31027 };
31028 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31029     rootVisible : true,
31030     animate: Roo.enableFx,
31031     lines : true,
31032     enableDD : false,
31033     hlDrop : Roo.enableFx,
31034   
31035     renderer: false,
31036     
31037     rendererTip: false,
31038     // private
31039     restrictExpand : function(node){
31040         var p = node.parentNode;
31041         if(p){
31042             if(p.expandedChild && p.expandedChild.parentNode == p){
31043                 p.expandedChild.collapse();
31044             }
31045             p.expandedChild = node;
31046         }
31047     },
31048
31049     // private override
31050     setRootNode : function(node){
31051         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31052         if(!this.rootVisible){
31053             node.ui = new Roo.tree.RootTreeNodeUI(node);
31054         }
31055         return node;
31056     },
31057
31058     /**
31059      * Returns the container element for this TreePanel
31060      */
31061     getEl : function(){
31062         return this.el;
31063     },
31064
31065     /**
31066      * Returns the default TreeLoader for this TreePanel
31067      */
31068     getLoader : function(){
31069         return this.loader;
31070     },
31071
31072     /**
31073      * Expand all nodes
31074      */
31075     expandAll : function(){
31076         this.root.expand(true);
31077     },
31078
31079     /**
31080      * Collapse all nodes
31081      */
31082     collapseAll : function(){
31083         this.root.collapse(true);
31084     },
31085
31086     /**
31087      * Returns the selection model used by this TreePanel
31088      */
31089     getSelectionModel : function(){
31090         if(!this.selModel){
31091             this.selModel = new Roo.tree.DefaultSelectionModel();
31092         }
31093         return this.selModel;
31094     },
31095
31096     /**
31097      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31098      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31099      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31100      * @return {Array}
31101      */
31102     getChecked : function(a, startNode){
31103         startNode = startNode || this.root;
31104         var r = [];
31105         var f = function(){
31106             if(this.attributes.checked){
31107                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31108             }
31109         }
31110         startNode.cascade(f);
31111         return r;
31112     },
31113
31114     /**
31115      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31116      * @param {String} path
31117      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31118      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31119      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31120      */
31121     expandPath : function(path, attr, callback){
31122         attr = attr || "id";
31123         var keys = path.split(this.pathSeparator);
31124         var curNode = this.root;
31125         if(curNode.attributes[attr] != keys[1]){ // invalid root
31126             if(callback){
31127                 callback(false, null);
31128             }
31129             return;
31130         }
31131         var index = 1;
31132         var f = function(){
31133             if(++index == keys.length){
31134                 if(callback){
31135                     callback(true, curNode);
31136                 }
31137                 return;
31138             }
31139             var c = curNode.findChild(attr, keys[index]);
31140             if(!c){
31141                 if(callback){
31142                     callback(false, curNode);
31143                 }
31144                 return;
31145             }
31146             curNode = c;
31147             c.expand(false, false, f);
31148         };
31149         curNode.expand(false, false, f);
31150     },
31151
31152     /**
31153      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31154      * @param {String} path
31155      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31156      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31157      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31158      */
31159     selectPath : function(path, attr, callback){
31160         attr = attr || "id";
31161         var keys = path.split(this.pathSeparator);
31162         var v = keys.pop();
31163         if(keys.length > 0){
31164             var f = function(success, node){
31165                 if(success && node){
31166                     var n = node.findChild(attr, v);
31167                     if(n){
31168                         n.select();
31169                         if(callback){
31170                             callback(true, n);
31171                         }
31172                     }else if(callback){
31173                         callback(false, n);
31174                     }
31175                 }else{
31176                     if(callback){
31177                         callback(false, n);
31178                     }
31179                 }
31180             };
31181             this.expandPath(keys.join(this.pathSeparator), attr, f);
31182         }else{
31183             this.root.select();
31184             if(callback){
31185                 callback(true, this.root);
31186             }
31187         }
31188     },
31189
31190     getTreeEl : function(){
31191         return this.el;
31192     },
31193
31194     /**
31195      * Trigger rendering of this TreePanel
31196      */
31197     render : function(){
31198         if (this.innerCt) {
31199             return this; // stop it rendering more than once!!
31200         }
31201         
31202         this.innerCt = this.el.createChild({tag:"ul",
31203                cls:"x-tree-root-ct " +
31204                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31205
31206         if(this.containerScroll){
31207             Roo.dd.ScrollManager.register(this.el);
31208         }
31209         if((this.enableDD || this.enableDrop) && !this.dropZone){
31210            /**
31211             * The dropZone used by this tree if drop is enabled
31212             * @type Roo.tree.TreeDropZone
31213             */
31214              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31215                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31216            });
31217         }
31218         if((this.enableDD || this.enableDrag) && !this.dragZone){
31219            /**
31220             * The dragZone used by this tree if drag is enabled
31221             * @type Roo.tree.TreeDragZone
31222             */
31223             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31224                ddGroup: this.ddGroup || "TreeDD",
31225                scroll: this.ddScroll
31226            });
31227         }
31228         this.getSelectionModel().init(this);
31229         if (!this.root) {
31230             console.log("ROOT not set in tree");
31231             return;
31232         }
31233         this.root.render();
31234         if(!this.rootVisible){
31235             this.root.renderChildren();
31236         }
31237         return this;
31238     }
31239 });/*
31240  * Based on:
31241  * Ext JS Library 1.1.1
31242  * Copyright(c) 2006-2007, Ext JS, LLC.
31243  *
31244  * Originally Released Under LGPL - original licence link has changed is not relivant.
31245  *
31246  * Fork - LGPL
31247  * <script type="text/javascript">
31248  */
31249  
31250
31251 /**
31252  * @class Roo.tree.DefaultSelectionModel
31253  * @extends Roo.util.Observable
31254  * The default single selection for a TreePanel.
31255  */
31256 Roo.tree.DefaultSelectionModel = function(){
31257    this.selNode = null;
31258    
31259    this.addEvents({
31260        /**
31261         * @event selectionchange
31262         * Fires when the selected node changes
31263         * @param {DefaultSelectionModel} this
31264         * @param {TreeNode} node the new selection
31265         */
31266        "selectionchange" : true,
31267
31268        /**
31269         * @event beforeselect
31270         * Fires before the selected node changes, return false to cancel the change
31271         * @param {DefaultSelectionModel} this
31272         * @param {TreeNode} node the new selection
31273         * @param {TreeNode} node the old selection
31274         */
31275        "beforeselect" : true
31276    });
31277 };
31278
31279 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31280     init : function(tree){
31281         this.tree = tree;
31282         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31283         tree.on("click", this.onNodeClick, this);
31284     },
31285     
31286     onNodeClick : function(node, e){
31287         if (e.ctrlKey && this.selNode == node)  {
31288             this.unselect(node);
31289             return;
31290         }
31291         this.select(node);
31292     },
31293     
31294     /**
31295      * Select a node.
31296      * @param {TreeNode} node The node to select
31297      * @return {TreeNode} The selected node
31298      */
31299     select : function(node){
31300         var last = this.selNode;
31301         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31302             if(last){
31303                 last.ui.onSelectedChange(false);
31304             }
31305             this.selNode = node;
31306             node.ui.onSelectedChange(true);
31307             this.fireEvent("selectionchange", this, node, last);
31308         }
31309         return node;
31310     },
31311     
31312     /**
31313      * Deselect a node.
31314      * @param {TreeNode} node The node to unselect
31315      */
31316     unselect : function(node){
31317         if(this.selNode == node){
31318             this.clearSelections();
31319         }    
31320     },
31321     
31322     /**
31323      * Clear all selections
31324      */
31325     clearSelections : function(){
31326         var n = this.selNode;
31327         if(n){
31328             n.ui.onSelectedChange(false);
31329             this.selNode = null;
31330             this.fireEvent("selectionchange", this, null);
31331         }
31332         return n;
31333     },
31334     
31335     /**
31336      * Get the selected node
31337      * @return {TreeNode} The selected node
31338      */
31339     getSelectedNode : function(){
31340         return this.selNode;    
31341     },
31342     
31343     /**
31344      * Returns true if the node is selected
31345      * @param {TreeNode} node The node to check
31346      * @return {Boolean}
31347      */
31348     isSelected : function(node){
31349         return this.selNode == node;  
31350     },
31351
31352     /**
31353      * Selects the node above the selected node in the tree, intelligently walking the nodes
31354      * @return TreeNode The new selection
31355      */
31356     selectPrevious : function(){
31357         var s = this.selNode || this.lastSelNode;
31358         if(!s){
31359             return null;
31360         }
31361         var ps = s.previousSibling;
31362         if(ps){
31363             if(!ps.isExpanded() || ps.childNodes.length < 1){
31364                 return this.select(ps);
31365             } else{
31366                 var lc = ps.lastChild;
31367                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31368                     lc = lc.lastChild;
31369                 }
31370                 return this.select(lc);
31371             }
31372         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31373             return this.select(s.parentNode);
31374         }
31375         return null;
31376     },
31377
31378     /**
31379      * Selects the node above the selected node in the tree, intelligently walking the nodes
31380      * @return TreeNode The new selection
31381      */
31382     selectNext : function(){
31383         var s = this.selNode || this.lastSelNode;
31384         if(!s){
31385             return null;
31386         }
31387         if(s.firstChild && s.isExpanded()){
31388              return this.select(s.firstChild);
31389          }else if(s.nextSibling){
31390              return this.select(s.nextSibling);
31391          }else if(s.parentNode){
31392             var newS = null;
31393             s.parentNode.bubble(function(){
31394                 if(this.nextSibling){
31395                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31396                     return false;
31397                 }
31398             });
31399             return newS;
31400          }
31401         return null;
31402     },
31403
31404     onKeyDown : function(e){
31405         var s = this.selNode || this.lastSelNode;
31406         // undesirable, but required
31407         var sm = this;
31408         if(!s){
31409             return;
31410         }
31411         var k = e.getKey();
31412         switch(k){
31413              case e.DOWN:
31414                  e.stopEvent();
31415                  this.selectNext();
31416              break;
31417              case e.UP:
31418                  e.stopEvent();
31419                  this.selectPrevious();
31420              break;
31421              case e.RIGHT:
31422                  e.preventDefault();
31423                  if(s.hasChildNodes()){
31424                      if(!s.isExpanded()){
31425                          s.expand();
31426                      }else if(s.firstChild){
31427                          this.select(s.firstChild, e);
31428                      }
31429                  }
31430              break;
31431              case e.LEFT:
31432                  e.preventDefault();
31433                  if(s.hasChildNodes() && s.isExpanded()){
31434                      s.collapse();
31435                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31436                      this.select(s.parentNode, e);
31437                  }
31438              break;
31439         };
31440     }
31441 });
31442
31443 /**
31444  * @class Roo.tree.MultiSelectionModel
31445  * @extends Roo.util.Observable
31446  * Multi selection for a TreePanel.
31447  */
31448 Roo.tree.MultiSelectionModel = function(){
31449    this.selNodes = [];
31450    this.selMap = {};
31451    this.addEvents({
31452        /**
31453         * @event selectionchange
31454         * Fires when the selected nodes change
31455         * @param {MultiSelectionModel} this
31456         * @param {Array} nodes Array of the selected nodes
31457         */
31458        "selectionchange" : true
31459    });
31460 };
31461
31462 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31463     init : function(tree){
31464         this.tree = tree;
31465         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31466         tree.on("click", this.onNodeClick, this);
31467     },
31468     
31469     onNodeClick : function(node, e){
31470         this.select(node, e, e.ctrlKey);
31471     },
31472     
31473     /**
31474      * Select a node.
31475      * @param {TreeNode} node The node to select
31476      * @param {EventObject} e (optional) An event associated with the selection
31477      * @param {Boolean} keepExisting True to retain existing selections
31478      * @return {TreeNode} The selected node
31479      */
31480     select : function(node, e, keepExisting){
31481         if(keepExisting !== true){
31482             this.clearSelections(true);
31483         }
31484         if(this.isSelected(node)){
31485             this.lastSelNode = node;
31486             return node;
31487         }
31488         this.selNodes.push(node);
31489         this.selMap[node.id] = node;
31490         this.lastSelNode = node;
31491         node.ui.onSelectedChange(true);
31492         this.fireEvent("selectionchange", this, this.selNodes);
31493         return node;
31494     },
31495     
31496     /**
31497      * Deselect a node.
31498      * @param {TreeNode} node The node to unselect
31499      */
31500     unselect : function(node){
31501         if(this.selMap[node.id]){
31502             node.ui.onSelectedChange(false);
31503             var sn = this.selNodes;
31504             var index = -1;
31505             if(sn.indexOf){
31506                 index = sn.indexOf(node);
31507             }else{
31508                 for(var i = 0, len = sn.length; i < len; i++){
31509                     if(sn[i] == node){
31510                         index = i;
31511                         break;
31512                     }
31513                 }
31514             }
31515             if(index != -1){
31516                 this.selNodes.splice(index, 1);
31517             }
31518             delete this.selMap[node.id];
31519             this.fireEvent("selectionchange", this, this.selNodes);
31520         }
31521     },
31522     
31523     /**
31524      * Clear all selections
31525      */
31526     clearSelections : function(suppressEvent){
31527         var sn = this.selNodes;
31528         if(sn.length > 0){
31529             for(var i = 0, len = sn.length; i < len; i++){
31530                 sn[i].ui.onSelectedChange(false);
31531             }
31532             this.selNodes = [];
31533             this.selMap = {};
31534             if(suppressEvent !== true){
31535                 this.fireEvent("selectionchange", this, this.selNodes);
31536             }
31537         }
31538     },
31539     
31540     /**
31541      * Returns true if the node is selected
31542      * @param {TreeNode} node The node to check
31543      * @return {Boolean}
31544      */
31545     isSelected : function(node){
31546         return this.selMap[node.id] ? true : false;  
31547     },
31548     
31549     /**
31550      * Returns an array of the selected nodes
31551      * @return {Array}
31552      */
31553     getSelectedNodes : function(){
31554         return this.selNodes;    
31555     },
31556
31557     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31558
31559     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31560
31561     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31562 });/*
31563  * Based on:
31564  * Ext JS Library 1.1.1
31565  * Copyright(c) 2006-2007, Ext JS, LLC.
31566  *
31567  * Originally Released Under LGPL - original licence link has changed is not relivant.
31568  *
31569  * Fork - LGPL
31570  * <script type="text/javascript">
31571  */
31572  
31573 /**
31574  * @class Roo.tree.TreeNode
31575  * @extends Roo.data.Node
31576  * @cfg {String} text The text for this node
31577  * @cfg {Boolean} expanded true to start the node expanded
31578  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31579  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31580  * @cfg {Boolean} disabled true to start the node disabled
31581  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31582  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31583  * @cfg {String} cls A css class to be added to the node
31584  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31585  * @cfg {String} href URL of the link used for the node (defaults to #)
31586  * @cfg {String} hrefTarget target frame for the link
31587  * @cfg {String} qtip An Ext QuickTip for the node
31588  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31589  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31590  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31591  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31592  * (defaults to undefined with no checkbox rendered)
31593  * @constructor
31594  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31595  */
31596 Roo.tree.TreeNode = function(attributes){
31597     attributes = attributes || {};
31598     if(typeof attributes == "string"){
31599         attributes = {text: attributes};
31600     }
31601     this.childrenRendered = false;
31602     this.rendered = false;
31603     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31604     this.expanded = attributes.expanded === true;
31605     this.isTarget = attributes.isTarget !== false;
31606     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31607     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31608
31609     /**
31610      * Read-only. The text for this node. To change it use setText().
31611      * @type String
31612      */
31613     this.text = attributes.text;
31614     /**
31615      * True if this node is disabled.
31616      * @type Boolean
31617      */
31618     this.disabled = attributes.disabled === true;
31619
31620     this.addEvents({
31621         /**
31622         * @event textchange
31623         * Fires when the text for this node is changed
31624         * @param {Node} this This node
31625         * @param {String} text The new text
31626         * @param {String} oldText The old text
31627         */
31628         "textchange" : true,
31629         /**
31630         * @event beforeexpand
31631         * Fires before this node is expanded, return false to cancel.
31632         * @param {Node} this This node
31633         * @param {Boolean} deep
31634         * @param {Boolean} anim
31635         */
31636         "beforeexpand" : true,
31637         /**
31638         * @event beforecollapse
31639         * Fires before this node is collapsed, return false to cancel.
31640         * @param {Node} this This node
31641         * @param {Boolean} deep
31642         * @param {Boolean} anim
31643         */
31644         "beforecollapse" : true,
31645         /**
31646         * @event expand
31647         * Fires when this node is expanded
31648         * @param {Node} this This node
31649         */
31650         "expand" : true,
31651         /**
31652         * @event disabledchange
31653         * Fires when the disabled status of this node changes
31654         * @param {Node} this This node
31655         * @param {Boolean} disabled
31656         */
31657         "disabledchange" : true,
31658         /**
31659         * @event collapse
31660         * Fires when this node is collapsed
31661         * @param {Node} this This node
31662         */
31663         "collapse" : true,
31664         /**
31665         * @event beforeclick
31666         * Fires before click processing. Return false to cancel the default action.
31667         * @param {Node} this This node
31668         * @param {Roo.EventObject} e The event object
31669         */
31670         "beforeclick":true,
31671         /**
31672         * @event checkchange
31673         * Fires when a node with a checkbox's checked property changes
31674         * @param {Node} this This node
31675         * @param {Boolean} checked
31676         */
31677         "checkchange":true,
31678         /**
31679         * @event click
31680         * Fires when this node is clicked
31681         * @param {Node} this This node
31682         * @param {Roo.EventObject} e The event object
31683         */
31684         "click":true,
31685         /**
31686         * @event dblclick
31687         * Fires when this node is double clicked
31688         * @param {Node} this This node
31689         * @param {Roo.EventObject} e The event object
31690         */
31691         "dblclick":true,
31692         /**
31693         * @event contextmenu
31694         * Fires when this node is right clicked
31695         * @param {Node} this This node
31696         * @param {Roo.EventObject} e The event object
31697         */
31698         "contextmenu":true,
31699         /**
31700         * @event beforechildrenrendered
31701         * Fires right before the child nodes for this node are rendered
31702         * @param {Node} this This node
31703         */
31704         "beforechildrenrendered":true
31705     });
31706
31707     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31708
31709     /**
31710      * Read-only. The UI for this node
31711      * @type TreeNodeUI
31712      */
31713     this.ui = new uiClass(this);
31714 };
31715 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31716     preventHScroll: true,
31717     /**
31718      * Returns true if this node is expanded
31719      * @return {Boolean}
31720      */
31721     isExpanded : function(){
31722         return this.expanded;
31723     },
31724
31725     /**
31726      * Returns the UI object for this node
31727      * @return {TreeNodeUI}
31728      */
31729     getUI : function(){
31730         return this.ui;
31731     },
31732
31733     // private override
31734     setFirstChild : function(node){
31735         var of = this.firstChild;
31736         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31737         if(this.childrenRendered && of && node != of){
31738             of.renderIndent(true, true);
31739         }
31740         if(this.rendered){
31741             this.renderIndent(true, true);
31742         }
31743     },
31744
31745     // private override
31746     setLastChild : function(node){
31747         var ol = this.lastChild;
31748         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31749         if(this.childrenRendered && ol && node != ol){
31750             ol.renderIndent(true, true);
31751         }
31752         if(this.rendered){
31753             this.renderIndent(true, true);
31754         }
31755     },
31756
31757     // these methods are overridden to provide lazy rendering support
31758     // private override
31759     appendChild : function(){
31760         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31761         if(node && this.childrenRendered){
31762             node.render();
31763         }
31764         this.ui.updateExpandIcon();
31765         return node;
31766     },
31767
31768     // private override
31769     removeChild : function(node){
31770         this.ownerTree.getSelectionModel().unselect(node);
31771         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31772         // if it's been rendered remove dom node
31773         if(this.childrenRendered){
31774             node.ui.remove();
31775         }
31776         if(this.childNodes.length < 1){
31777             this.collapse(false, false);
31778         }else{
31779             this.ui.updateExpandIcon();
31780         }
31781         if(!this.firstChild) {
31782             this.childrenRendered = false;
31783         }
31784         return node;
31785     },
31786
31787     // private override
31788     insertBefore : function(node, refNode){
31789         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31790         if(newNode && refNode && this.childrenRendered){
31791             node.render();
31792         }
31793         this.ui.updateExpandIcon();
31794         return newNode;
31795     },
31796
31797     /**
31798      * Sets the text for this node
31799      * @param {String} text
31800      */
31801     setText : function(text){
31802         var oldText = this.text;
31803         this.text = text;
31804         this.attributes.text = text;
31805         if(this.rendered){ // event without subscribing
31806             this.ui.onTextChange(this, text, oldText);
31807         }
31808         this.fireEvent("textchange", this, text, oldText);
31809     },
31810
31811     /**
31812      * Triggers selection of this node
31813      */
31814     select : function(){
31815         this.getOwnerTree().getSelectionModel().select(this);
31816     },
31817
31818     /**
31819      * Triggers deselection of this node
31820      */
31821     unselect : function(){
31822         this.getOwnerTree().getSelectionModel().unselect(this);
31823     },
31824
31825     /**
31826      * Returns true if this node is selected
31827      * @return {Boolean}
31828      */
31829     isSelected : function(){
31830         return this.getOwnerTree().getSelectionModel().isSelected(this);
31831     },
31832
31833     /**
31834      * Expand this node.
31835      * @param {Boolean} deep (optional) True to expand all children as well
31836      * @param {Boolean} anim (optional) false to cancel the default animation
31837      * @param {Function} callback (optional) A callback to be called when
31838      * expanding this node completes (does not wait for deep expand to complete).
31839      * Called with 1 parameter, this node.
31840      */
31841     expand : function(deep, anim, callback){
31842         if(!this.expanded){
31843             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31844                 return;
31845             }
31846             if(!this.childrenRendered){
31847                 this.renderChildren();
31848             }
31849             this.expanded = true;
31850             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31851                 this.ui.animExpand(function(){
31852                     this.fireEvent("expand", this);
31853                     if(typeof callback == "function"){
31854                         callback(this);
31855                     }
31856                     if(deep === true){
31857                         this.expandChildNodes(true);
31858                     }
31859                 }.createDelegate(this));
31860                 return;
31861             }else{
31862                 this.ui.expand();
31863                 this.fireEvent("expand", this);
31864                 if(typeof callback == "function"){
31865                     callback(this);
31866                 }
31867             }
31868         }else{
31869            if(typeof callback == "function"){
31870                callback(this);
31871            }
31872         }
31873         if(deep === true){
31874             this.expandChildNodes(true);
31875         }
31876     },
31877
31878     isHiddenRoot : function(){
31879         return this.isRoot && !this.getOwnerTree().rootVisible;
31880     },
31881
31882     /**
31883      * Collapse this node.
31884      * @param {Boolean} deep (optional) True to collapse all children as well
31885      * @param {Boolean} anim (optional) false to cancel the default animation
31886      */
31887     collapse : function(deep, anim){
31888         if(this.expanded && !this.isHiddenRoot()){
31889             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31890                 return;
31891             }
31892             this.expanded = false;
31893             if((this.getOwnerTree().animate && anim !== false) || anim){
31894                 this.ui.animCollapse(function(){
31895                     this.fireEvent("collapse", this);
31896                     if(deep === true){
31897                         this.collapseChildNodes(true);
31898                     }
31899                 }.createDelegate(this));
31900                 return;
31901             }else{
31902                 this.ui.collapse();
31903                 this.fireEvent("collapse", this);
31904             }
31905         }
31906         if(deep === true){
31907             var cs = this.childNodes;
31908             for(var i = 0, len = cs.length; i < len; i++) {
31909                 cs[i].collapse(true, false);
31910             }
31911         }
31912     },
31913
31914     // private
31915     delayedExpand : function(delay){
31916         if(!this.expandProcId){
31917             this.expandProcId = this.expand.defer(delay, this);
31918         }
31919     },
31920
31921     // private
31922     cancelExpand : function(){
31923         if(this.expandProcId){
31924             clearTimeout(this.expandProcId);
31925         }
31926         this.expandProcId = false;
31927     },
31928
31929     /**
31930      * Toggles expanded/collapsed state of the node
31931      */
31932     toggle : function(){
31933         if(this.expanded){
31934             this.collapse();
31935         }else{
31936             this.expand();
31937         }
31938     },
31939
31940     /**
31941      * Ensures all parent nodes are expanded
31942      */
31943     ensureVisible : function(callback){
31944         var tree = this.getOwnerTree();
31945         tree.expandPath(this.parentNode.getPath(), false, function(){
31946             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31947             Roo.callback(callback);
31948         }.createDelegate(this));
31949     },
31950
31951     /**
31952      * Expand all child nodes
31953      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31954      */
31955     expandChildNodes : function(deep){
31956         var cs = this.childNodes;
31957         for(var i = 0, len = cs.length; i < len; i++) {
31958                 cs[i].expand(deep);
31959         }
31960     },
31961
31962     /**
31963      * Collapse all child nodes
31964      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31965      */
31966     collapseChildNodes : function(deep){
31967         var cs = this.childNodes;
31968         for(var i = 0, len = cs.length; i < len; i++) {
31969                 cs[i].collapse(deep);
31970         }
31971     },
31972
31973     /**
31974      * Disables this node
31975      */
31976     disable : function(){
31977         this.disabled = true;
31978         this.unselect();
31979         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31980             this.ui.onDisableChange(this, true);
31981         }
31982         this.fireEvent("disabledchange", this, true);
31983     },
31984
31985     /**
31986      * Enables this node
31987      */
31988     enable : function(){
31989         this.disabled = false;
31990         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31991             this.ui.onDisableChange(this, false);
31992         }
31993         this.fireEvent("disabledchange", this, false);
31994     },
31995
31996     // private
31997     renderChildren : function(suppressEvent){
31998         if(suppressEvent !== false){
31999             this.fireEvent("beforechildrenrendered", this);
32000         }
32001         var cs = this.childNodes;
32002         for(var i = 0, len = cs.length; i < len; i++){
32003             cs[i].render(true);
32004         }
32005         this.childrenRendered = true;
32006     },
32007
32008     // private
32009     sort : function(fn, scope){
32010         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32011         if(this.childrenRendered){
32012             var cs = this.childNodes;
32013             for(var i = 0, len = cs.length; i < len; i++){
32014                 cs[i].render(true);
32015             }
32016         }
32017     },
32018
32019     // private
32020     render : function(bulkRender){
32021         this.ui.render(bulkRender);
32022         if(!this.rendered){
32023             this.rendered = true;
32024             if(this.expanded){
32025                 this.expanded = false;
32026                 this.expand(false, false);
32027             }
32028         }
32029     },
32030
32031     // private
32032     renderIndent : function(deep, refresh){
32033         if(refresh){
32034             this.ui.childIndent = null;
32035         }
32036         this.ui.renderIndent();
32037         if(deep === true && this.childrenRendered){
32038             var cs = this.childNodes;
32039             for(var i = 0, len = cs.length; i < len; i++){
32040                 cs[i].renderIndent(true, refresh);
32041             }
32042         }
32043     }
32044 });/*
32045  * Based on:
32046  * Ext JS Library 1.1.1
32047  * Copyright(c) 2006-2007, Ext JS, LLC.
32048  *
32049  * Originally Released Under LGPL - original licence link has changed is not relivant.
32050  *
32051  * Fork - LGPL
32052  * <script type="text/javascript">
32053  */
32054  
32055 /**
32056  * @class Roo.tree.AsyncTreeNode
32057  * @extends Roo.tree.TreeNode
32058  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32059  * @constructor
32060  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32061  */
32062  Roo.tree.AsyncTreeNode = function(config){
32063     this.loaded = false;
32064     this.loading = false;
32065     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32066     /**
32067     * @event beforeload
32068     * Fires before this node is loaded, return false to cancel
32069     * @param {Node} this This node
32070     */
32071     this.addEvents({'beforeload':true, 'load': true});
32072     /**
32073     * @event load
32074     * Fires when this node is loaded
32075     * @param {Node} this This node
32076     */
32077     /**
32078      * The loader used by this node (defaults to using the tree's defined loader)
32079      * @type TreeLoader
32080      * @property loader
32081      */
32082 };
32083 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32084     expand : function(deep, anim, callback){
32085         if(this.loading){ // if an async load is already running, waiting til it's done
32086             var timer;
32087             var f = function(){
32088                 if(!this.loading){ // done loading
32089                     clearInterval(timer);
32090                     this.expand(deep, anim, callback);
32091                 }
32092             }.createDelegate(this);
32093             timer = setInterval(f, 200);
32094             return;
32095         }
32096         if(!this.loaded){
32097             if(this.fireEvent("beforeload", this) === false){
32098                 return;
32099             }
32100             this.loading = true;
32101             this.ui.beforeLoad(this);
32102             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32103             if(loader){
32104                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32105                 return;
32106             }
32107         }
32108         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32109     },
32110     
32111     /**
32112      * Returns true if this node is currently loading
32113      * @return {Boolean}
32114      */
32115     isLoading : function(){
32116         return this.loading;  
32117     },
32118     
32119     loadComplete : function(deep, anim, callback){
32120         this.loading = false;
32121         this.loaded = true;
32122         this.ui.afterLoad(this);
32123         this.fireEvent("load", this);
32124         this.expand(deep, anim, callback);
32125     },
32126     
32127     /**
32128      * Returns true if this node has been loaded
32129      * @return {Boolean}
32130      */
32131     isLoaded : function(){
32132         return this.loaded;
32133     },
32134     
32135     hasChildNodes : function(){
32136         if(!this.isLeaf() && !this.loaded){
32137             return true;
32138         }else{
32139             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32140         }
32141     },
32142
32143     /**
32144      * Trigger a reload for this node
32145      * @param {Function} callback
32146      */
32147     reload : function(callback){
32148         this.collapse(false, false);
32149         while(this.firstChild){
32150             this.removeChild(this.firstChild);
32151         }
32152         this.childrenRendered = false;
32153         this.loaded = false;
32154         if(this.isHiddenRoot()){
32155             this.expanded = false;
32156         }
32157         this.expand(false, false, callback);
32158     }
32159 });/*
32160  * Based on:
32161  * Ext JS Library 1.1.1
32162  * Copyright(c) 2006-2007, Ext JS, LLC.
32163  *
32164  * Originally Released Under LGPL - original licence link has changed is not relivant.
32165  *
32166  * Fork - LGPL
32167  * <script type="text/javascript">
32168  */
32169  
32170 /**
32171  * @class Roo.tree.TreeNodeUI
32172  * @constructor
32173  * @param {Object} node The node to render
32174  * The TreeNode UI implementation is separate from the
32175  * tree implementation. Unless you are customizing the tree UI,
32176  * you should never have to use this directly.
32177  */
32178 Roo.tree.TreeNodeUI = function(node){
32179     this.node = node;
32180     this.rendered = false;
32181     this.animating = false;
32182     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32183 };
32184
32185 Roo.tree.TreeNodeUI.prototype = {
32186     removeChild : function(node){
32187         if(this.rendered){
32188             this.ctNode.removeChild(node.ui.getEl());
32189         }
32190     },
32191
32192     beforeLoad : function(){
32193          this.addClass("x-tree-node-loading");
32194     },
32195
32196     afterLoad : function(){
32197          this.removeClass("x-tree-node-loading");
32198     },
32199
32200     onTextChange : function(node, text, oldText){
32201         if(this.rendered){
32202             this.textNode.innerHTML = text;
32203         }
32204     },
32205
32206     onDisableChange : function(node, state){
32207         this.disabled = state;
32208         if(state){
32209             this.addClass("x-tree-node-disabled");
32210         }else{
32211             this.removeClass("x-tree-node-disabled");
32212         }
32213     },
32214
32215     onSelectedChange : function(state){
32216         if(state){
32217             this.focus();
32218             this.addClass("x-tree-selected");
32219         }else{
32220             //this.blur();
32221             this.removeClass("x-tree-selected");
32222         }
32223     },
32224
32225     onMove : function(tree, node, oldParent, newParent, index, refNode){
32226         this.childIndent = null;
32227         if(this.rendered){
32228             var targetNode = newParent.ui.getContainer();
32229             if(!targetNode){//target not rendered
32230                 this.holder = document.createElement("div");
32231                 this.holder.appendChild(this.wrap);
32232                 return;
32233             }
32234             var insertBefore = refNode ? refNode.ui.getEl() : null;
32235             if(insertBefore){
32236                 targetNode.insertBefore(this.wrap, insertBefore);
32237             }else{
32238                 targetNode.appendChild(this.wrap);
32239             }
32240             this.node.renderIndent(true);
32241         }
32242     },
32243
32244     addClass : function(cls){
32245         if(this.elNode){
32246             Roo.fly(this.elNode).addClass(cls);
32247         }
32248     },
32249
32250     removeClass : function(cls){
32251         if(this.elNode){
32252             Roo.fly(this.elNode).removeClass(cls);
32253         }
32254     },
32255
32256     remove : function(){
32257         if(this.rendered){
32258             this.holder = document.createElement("div");
32259             this.holder.appendChild(this.wrap);
32260         }
32261     },
32262
32263     fireEvent : function(){
32264         return this.node.fireEvent.apply(this.node, arguments);
32265     },
32266
32267     initEvents : function(){
32268         this.node.on("move", this.onMove, this);
32269         var E = Roo.EventManager;
32270         var a = this.anchor;
32271
32272         var el = Roo.fly(a, '_treeui');
32273
32274         if(Roo.isOpera){ // opera render bug ignores the CSS
32275             el.setStyle("text-decoration", "none");
32276         }
32277
32278         el.on("click", this.onClick, this);
32279         el.on("dblclick", this.onDblClick, this);
32280
32281         if(this.checkbox){
32282             Roo.EventManager.on(this.checkbox,
32283                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32284         }
32285
32286         el.on("contextmenu", this.onContextMenu, this);
32287
32288         var icon = Roo.fly(this.iconNode);
32289         icon.on("click", this.onClick, this);
32290         icon.on("dblclick", this.onDblClick, this);
32291         icon.on("contextmenu", this.onContextMenu, this);
32292         E.on(this.ecNode, "click", this.ecClick, this, true);
32293
32294         if(this.node.disabled){
32295             this.addClass("x-tree-node-disabled");
32296         }
32297         if(this.node.hidden){
32298             this.addClass("x-tree-node-disabled");
32299         }
32300         var ot = this.node.getOwnerTree();
32301         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32302         if(dd && (!this.node.isRoot || ot.rootVisible)){
32303             Roo.dd.Registry.register(this.elNode, {
32304                 node: this.node,
32305                 handles: this.getDDHandles(),
32306                 isHandle: false
32307             });
32308         }
32309     },
32310
32311     getDDHandles : function(){
32312         return [this.iconNode, this.textNode];
32313     },
32314
32315     hide : function(){
32316         if(this.rendered){
32317             this.wrap.style.display = "none";
32318         }
32319     },
32320
32321     show : function(){
32322         if(this.rendered){
32323             this.wrap.style.display = "";
32324         }
32325     },
32326
32327     onContextMenu : function(e){
32328         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32329             e.preventDefault();
32330             this.focus();
32331             this.fireEvent("contextmenu", this.node, e);
32332         }
32333     },
32334
32335     onClick : function(e){
32336         if(this.dropping){
32337             e.stopEvent();
32338             return;
32339         }
32340         if(this.fireEvent("beforeclick", this.node, e) !== false){
32341             if(!this.disabled && this.node.attributes.href){
32342                 this.fireEvent("click", this.node, e);
32343                 return;
32344             }
32345             e.preventDefault();
32346             if(this.disabled){
32347                 return;
32348             }
32349
32350             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32351                 this.node.toggle();
32352             }
32353
32354             this.fireEvent("click", this.node, e);
32355         }else{
32356             e.stopEvent();
32357         }
32358     },
32359
32360     onDblClick : function(e){
32361         e.preventDefault();
32362         if(this.disabled){
32363             return;
32364         }
32365         if(this.checkbox){
32366             this.toggleCheck();
32367         }
32368         if(!this.animating && this.node.hasChildNodes()){
32369             this.node.toggle();
32370         }
32371         this.fireEvent("dblclick", this.node, e);
32372     },
32373
32374     onCheckChange : function(){
32375         var checked = this.checkbox.checked;
32376         this.node.attributes.checked = checked;
32377         this.fireEvent('checkchange', this.node, checked);
32378     },
32379
32380     ecClick : function(e){
32381         if(!this.animating && this.node.hasChildNodes()){
32382             this.node.toggle();
32383         }
32384     },
32385
32386     startDrop : function(){
32387         this.dropping = true;
32388     },
32389
32390     // delayed drop so the click event doesn't get fired on a drop
32391     endDrop : function(){
32392        setTimeout(function(){
32393            this.dropping = false;
32394        }.createDelegate(this), 50);
32395     },
32396
32397     expand : function(){
32398         this.updateExpandIcon();
32399         this.ctNode.style.display = "";
32400     },
32401
32402     focus : function(){
32403         if(!this.node.preventHScroll){
32404             try{this.anchor.focus();
32405             }catch(e){}
32406         }else if(!Roo.isIE){
32407             try{
32408                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32409                 var l = noscroll.scrollLeft;
32410                 this.anchor.focus();
32411                 noscroll.scrollLeft = l;
32412             }catch(e){}
32413         }
32414     },
32415
32416     toggleCheck : function(value){
32417         var cb = this.checkbox;
32418         if(cb){
32419             cb.checked = (value === undefined ? !cb.checked : value);
32420         }
32421     },
32422
32423     blur : function(){
32424         try{
32425             this.anchor.blur();
32426         }catch(e){}
32427     },
32428
32429     animExpand : function(callback){
32430         var ct = Roo.get(this.ctNode);
32431         ct.stopFx();
32432         if(!this.node.hasChildNodes()){
32433             this.updateExpandIcon();
32434             this.ctNode.style.display = "";
32435             Roo.callback(callback);
32436             return;
32437         }
32438         this.animating = true;
32439         this.updateExpandIcon();
32440
32441         ct.slideIn('t', {
32442            callback : function(){
32443                this.animating = false;
32444                Roo.callback(callback);
32445             },
32446             scope: this,
32447             duration: this.node.ownerTree.duration || .25
32448         });
32449     },
32450
32451     highlight : function(){
32452         var tree = this.node.getOwnerTree();
32453         Roo.fly(this.wrap).highlight(
32454             tree.hlColor || "C3DAF9",
32455             {endColor: tree.hlBaseColor}
32456         );
32457     },
32458
32459     collapse : function(){
32460         this.updateExpandIcon();
32461         this.ctNode.style.display = "none";
32462     },
32463
32464     animCollapse : function(callback){
32465         var ct = Roo.get(this.ctNode);
32466         ct.enableDisplayMode('block');
32467         ct.stopFx();
32468
32469         this.animating = true;
32470         this.updateExpandIcon();
32471
32472         ct.slideOut('t', {
32473             callback : function(){
32474                this.animating = false;
32475                Roo.callback(callback);
32476             },
32477             scope: this,
32478             duration: this.node.ownerTree.duration || .25
32479         });
32480     },
32481
32482     getContainer : function(){
32483         return this.ctNode;
32484     },
32485
32486     getEl : function(){
32487         return this.wrap;
32488     },
32489
32490     appendDDGhost : function(ghostNode){
32491         ghostNode.appendChild(this.elNode.cloneNode(true));
32492     },
32493
32494     getDDRepairXY : function(){
32495         return Roo.lib.Dom.getXY(this.iconNode);
32496     },
32497
32498     onRender : function(){
32499         this.render();
32500     },
32501
32502     render : function(bulkRender){
32503         var n = this.node, a = n.attributes;
32504         var targetNode = n.parentNode ?
32505               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32506
32507         if(!this.rendered){
32508             this.rendered = true;
32509
32510             this.renderElements(n, a, targetNode, bulkRender);
32511
32512             if(a.qtip){
32513                if(this.textNode.setAttributeNS){
32514                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32515                    if(a.qtipTitle){
32516                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32517                    }
32518                }else{
32519                    this.textNode.setAttribute("ext:qtip", a.qtip);
32520                    if(a.qtipTitle){
32521                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32522                    }
32523                }
32524             }else if(a.qtipCfg){
32525                 a.qtipCfg.target = Roo.id(this.textNode);
32526                 Roo.QuickTips.register(a.qtipCfg);
32527             }
32528             this.initEvents();
32529             if(!this.node.expanded){
32530                 this.updateExpandIcon();
32531             }
32532         }else{
32533             if(bulkRender === true) {
32534                 targetNode.appendChild(this.wrap);
32535             }
32536         }
32537     },
32538
32539     renderElements : function(n, a, targetNode, bulkRender){
32540         // add some indent caching, this helps performance when rendering a large tree
32541         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32542         var t = n.getOwnerTree();
32543         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32544         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32545         var cb = typeof a.checked == 'boolean';
32546         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32547         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32548             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32549             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32550             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32551             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32552             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32553              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32554                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32555             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32556             "</li>"];
32557
32558         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32559             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32560                                 n.nextSibling.ui.getEl(), buf.join(""));
32561         }else{
32562             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32563         }
32564
32565         this.elNode = this.wrap.childNodes[0];
32566         this.ctNode = this.wrap.childNodes[1];
32567         var cs = this.elNode.childNodes;
32568         this.indentNode = cs[0];
32569         this.ecNode = cs[1];
32570         this.iconNode = cs[2];
32571         var index = 3;
32572         if(cb){
32573             this.checkbox = cs[3];
32574             index++;
32575         }
32576         this.anchor = cs[index];
32577         this.textNode = cs[index].firstChild;
32578     },
32579
32580     getAnchor : function(){
32581         return this.anchor;
32582     },
32583
32584     getTextEl : function(){
32585         return this.textNode;
32586     },
32587
32588     getIconEl : function(){
32589         return this.iconNode;
32590     },
32591
32592     isChecked : function(){
32593         return this.checkbox ? this.checkbox.checked : false;
32594     },
32595
32596     updateExpandIcon : function(){
32597         if(this.rendered){
32598             var n = this.node, c1, c2;
32599             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32600             var hasChild = n.hasChildNodes();
32601             if(hasChild){
32602                 if(n.expanded){
32603                     cls += "-minus";
32604                     c1 = "x-tree-node-collapsed";
32605                     c2 = "x-tree-node-expanded";
32606                 }else{
32607                     cls += "-plus";
32608                     c1 = "x-tree-node-expanded";
32609                     c2 = "x-tree-node-collapsed";
32610                 }
32611                 if(this.wasLeaf){
32612                     this.removeClass("x-tree-node-leaf");
32613                     this.wasLeaf = false;
32614                 }
32615                 if(this.c1 != c1 || this.c2 != c2){
32616                     Roo.fly(this.elNode).replaceClass(c1, c2);
32617                     this.c1 = c1; this.c2 = c2;
32618                 }
32619             }else{
32620                 if(!this.wasLeaf){
32621                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32622                     delete this.c1;
32623                     delete this.c2;
32624                     this.wasLeaf = true;
32625                 }
32626             }
32627             var ecc = "x-tree-ec-icon "+cls;
32628             if(this.ecc != ecc){
32629                 this.ecNode.className = ecc;
32630                 this.ecc = ecc;
32631             }
32632         }
32633     },
32634
32635     getChildIndent : function(){
32636         if(!this.childIndent){
32637             var buf = [];
32638             var p = this.node;
32639             while(p){
32640                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32641                     if(!p.isLast()) {
32642                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32643                     } else {
32644                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32645                     }
32646                 }
32647                 p = p.parentNode;
32648             }
32649             this.childIndent = buf.join("");
32650         }
32651         return this.childIndent;
32652     },
32653
32654     renderIndent : function(){
32655         if(this.rendered){
32656             var indent = "";
32657             var p = this.node.parentNode;
32658             if(p){
32659                 indent = p.ui.getChildIndent();
32660             }
32661             if(this.indentMarkup != indent){ // don't rerender if not required
32662                 this.indentNode.innerHTML = indent;
32663                 this.indentMarkup = indent;
32664             }
32665             this.updateExpandIcon();
32666         }
32667     }
32668 };
32669
32670 Roo.tree.RootTreeNodeUI = function(){
32671     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32672 };
32673 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32674     render : function(){
32675         if(!this.rendered){
32676             var targetNode = this.node.ownerTree.innerCt.dom;
32677             this.node.expanded = true;
32678             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32679             this.wrap = this.ctNode = targetNode.firstChild;
32680         }
32681     },
32682     collapse : function(){
32683     },
32684     expand : function(){
32685     }
32686 });/*
32687  * Based on:
32688  * Ext JS Library 1.1.1
32689  * Copyright(c) 2006-2007, Ext JS, LLC.
32690  *
32691  * Originally Released Under LGPL - original licence link has changed is not relivant.
32692  *
32693  * Fork - LGPL
32694  * <script type="text/javascript">
32695  */
32696 /**
32697  * @class Roo.tree.TreeLoader
32698  * @extends Roo.util.Observable
32699  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32700  * nodes from a specified URL. The response must be a javascript Array definition
32701  * who's elements are node definition objects. eg:
32702  * <pre><code>
32703    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32704     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32705 </code></pre>
32706  * <br><br>
32707  * A server request is sent, and child nodes are loaded only when a node is expanded.
32708  * The loading node's id is passed to the server under the parameter name "node" to
32709  * enable the server to produce the correct child nodes.
32710  * <br><br>
32711  * To pass extra parameters, an event handler may be attached to the "beforeload"
32712  * event, and the parameters specified in the TreeLoader's baseParams property:
32713  * <pre><code>
32714     myTreeLoader.on("beforeload", function(treeLoader, node) {
32715         this.baseParams.category = node.attributes.category;
32716     }, this);
32717 </code></pre><
32718  * This would pass an HTTP parameter called "category" to the server containing
32719  * the value of the Node's "category" attribute.
32720  * @constructor
32721  * Creates a new Treeloader.
32722  * @param {Object} config A config object containing config properties.
32723  */
32724 Roo.tree.TreeLoader = function(config){
32725     this.baseParams = {};
32726     this.requestMethod = "POST";
32727     Roo.apply(this, config);
32728
32729     this.addEvents({
32730     
32731         /**
32732          * @event beforeload
32733          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32734          * @param {Object} This TreeLoader object.
32735          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32736          * @param {Object} callback The callback function specified in the {@link #load} call.
32737          */
32738         beforeload : true,
32739         /**
32740          * @event load
32741          * Fires when the node has been successfuly loaded.
32742          * @param {Object} This TreeLoader object.
32743          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32744          * @param {Object} response The response object containing the data from the server.
32745          */
32746         load : true,
32747         /**
32748          * @event loadexception
32749          * Fires if the network request failed.
32750          * @param {Object} This TreeLoader object.
32751          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32752          * @param {Object} response The response object containing the data from the server.
32753          */
32754         loadexception : true,
32755         /**
32756          * @event create
32757          * Fires before a node is created, enabling you to return custom Node types 
32758          * @param {Object} This TreeLoader object.
32759          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32760          */
32761         create : true
32762     });
32763
32764     Roo.tree.TreeLoader.superclass.constructor.call(this);
32765 };
32766
32767 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32768     /**
32769     * @cfg {String} dataUrl The URL from which to request a Json string which
32770     * specifies an array of node definition object representing the child nodes
32771     * to be loaded.
32772     */
32773     /**
32774     * @cfg {Object} baseParams (optional) An object containing properties which
32775     * specify HTTP parameters to be passed to each request for child nodes.
32776     */
32777     /**
32778     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32779     * created by this loader. If the attributes sent by the server have an attribute in this object,
32780     * they take priority.
32781     */
32782     /**
32783     * @cfg {Object} uiProviders (optional) An object containing properties which
32784     * 
32785     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32786     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32787     * <i>uiProvider</i> attribute of a returned child node is a string rather
32788     * than a reference to a TreeNodeUI implementation, this that string value
32789     * is used as a property name in the uiProviders object. You can define the provider named
32790     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32791     */
32792     uiProviders : {},
32793
32794     /**
32795     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32796     * child nodes before loading.
32797     */
32798     clearOnLoad : true,
32799
32800     /**
32801     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32802     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32803     * Grid query { data : [ .....] }
32804     */
32805     
32806     root : false,
32807      /**
32808     * @cfg {String} queryParam (optional) 
32809     * Name of the query as it will be passed on the querystring (defaults to 'node')
32810     * eg. the request will be ?node=[id]
32811     */
32812     
32813     
32814     queryParam: false,
32815     
32816     /**
32817      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32818      * This is called automatically when a node is expanded, but may be used to reload
32819      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32820      * @param {Roo.tree.TreeNode} node
32821      * @param {Function} callback
32822      */
32823     load : function(node, callback){
32824         if(this.clearOnLoad){
32825             while(node.firstChild){
32826                 node.removeChild(node.firstChild);
32827             }
32828         }
32829         if(node.attributes.children){ // preloaded json children
32830             var cs = node.attributes.children;
32831             for(var i = 0, len = cs.length; i < len; i++){
32832                 node.appendChild(this.createNode(cs[i]));
32833             }
32834             if(typeof callback == "function"){
32835                 callback();
32836             }
32837         }else if(this.dataUrl){
32838             this.requestData(node, callback);
32839         }
32840     },
32841
32842     getParams: function(node){
32843         var buf = [], bp = this.baseParams;
32844         for(var key in bp){
32845             if(typeof bp[key] != "function"){
32846                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32847             }
32848         }
32849         var n = this.queryParam === false ? 'node' : this.queryParam;
32850         buf.push(n + "=", encodeURIComponent(node.id));
32851         return buf.join("");
32852     },
32853
32854     requestData : function(node, callback){
32855         if(this.fireEvent("beforeload", this, node, callback) !== false){
32856             this.transId = Roo.Ajax.request({
32857                 method:this.requestMethod,
32858                 url: this.dataUrl||this.url,
32859                 success: this.handleResponse,
32860                 failure: this.handleFailure,
32861                 scope: this,
32862                 argument: {callback: callback, node: node},
32863                 params: this.getParams(node)
32864             });
32865         }else{
32866             // if the load is cancelled, make sure we notify
32867             // the node that we are done
32868             if(typeof callback == "function"){
32869                 callback();
32870             }
32871         }
32872     },
32873
32874     isLoading : function(){
32875         return this.transId ? true : false;
32876     },
32877
32878     abort : function(){
32879         if(this.isLoading()){
32880             Roo.Ajax.abort(this.transId);
32881         }
32882     },
32883
32884     // private
32885     createNode : function(attr){
32886         // apply baseAttrs, nice idea Corey!
32887         if(this.baseAttrs){
32888             Roo.applyIf(attr, this.baseAttrs);
32889         }
32890         if(this.applyLoader !== false){
32891             attr.loader = this;
32892         }
32893         // uiProvider = depreciated..
32894         
32895         if(typeof(attr.uiProvider) == 'string'){
32896            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32897                 /**  eval:var:attr */ eval(attr.uiProvider);
32898         }
32899         if(typeof(this.uiProviders['default']) != 'undefined') {
32900             attr.uiProvider = this.uiProviders['default'];
32901         }
32902         
32903         this.fireEvent('create', this, attr);
32904         
32905         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32906         return(attr.leaf ?
32907                         new Roo.tree.TreeNode(attr) :
32908                         new Roo.tree.AsyncTreeNode(attr));
32909     },
32910
32911     processResponse : function(response, node, callback){
32912         var json = response.responseText;
32913         try {
32914             
32915             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32916             if (this.root !== false) {
32917                 o = o[this.root];
32918             }
32919             
32920             for(var i = 0, len = o.length; i < len; i++){
32921                 var n = this.createNode(o[i]);
32922                 if(n){
32923                     node.appendChild(n);
32924                 }
32925             }
32926             if(typeof callback == "function"){
32927                 callback(this, node);
32928             }
32929         }catch(e){
32930             this.handleFailure(response);
32931         }
32932     },
32933
32934     handleResponse : function(response){
32935         this.transId = false;
32936         var a = response.argument;
32937         this.processResponse(response, a.node, a.callback);
32938         this.fireEvent("load", this, a.node, response);
32939     },
32940
32941     handleFailure : function(response){
32942         this.transId = false;
32943         var a = response.argument;
32944         this.fireEvent("loadexception", this, a.node, response);
32945         if(typeof a.callback == "function"){
32946             a.callback(this, a.node);
32947         }
32948     }
32949 });/*
32950  * Based on:
32951  * Ext JS Library 1.1.1
32952  * Copyright(c) 2006-2007, Ext JS, LLC.
32953  *
32954  * Originally Released Under LGPL - original licence link has changed is not relivant.
32955  *
32956  * Fork - LGPL
32957  * <script type="text/javascript">
32958  */
32959
32960 /**
32961 * @class Roo.tree.TreeFilter
32962 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32963 * @param {TreePanel} tree
32964 * @param {Object} config (optional)
32965  */
32966 Roo.tree.TreeFilter = function(tree, config){
32967     this.tree = tree;
32968     this.filtered = {};
32969     Roo.apply(this, config);
32970 };
32971
32972 Roo.tree.TreeFilter.prototype = {
32973     clearBlank:false,
32974     reverse:false,
32975     autoClear:false,
32976     remove:false,
32977
32978      /**
32979      * Filter the data by a specific attribute.
32980      * @param {String/RegExp} value Either string that the attribute value
32981      * should start with or a RegExp to test against the attribute
32982      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32983      * @param {TreeNode} startNode (optional) The node to start the filter at.
32984      */
32985     filter : function(value, attr, startNode){
32986         attr = attr || "text";
32987         var f;
32988         if(typeof value == "string"){
32989             var vlen = value.length;
32990             // auto clear empty filter
32991             if(vlen == 0 && this.clearBlank){
32992                 this.clear();
32993                 return;
32994             }
32995             value = value.toLowerCase();
32996             f = function(n){
32997                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32998             };
32999         }else if(value.exec){ // regex?
33000             f = function(n){
33001                 return value.test(n.attributes[attr]);
33002             };
33003         }else{
33004             throw 'Illegal filter type, must be string or regex';
33005         }
33006         this.filterBy(f, null, startNode);
33007         },
33008
33009     /**
33010      * Filter by a function. The passed function will be called with each
33011      * node in the tree (or from the startNode). If the function returns true, the node is kept
33012      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33013      * @param {Function} fn The filter function
33014      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33015      */
33016     filterBy : function(fn, scope, startNode){
33017         startNode = startNode || this.tree.root;
33018         if(this.autoClear){
33019             this.clear();
33020         }
33021         var af = this.filtered, rv = this.reverse;
33022         var f = function(n){
33023             if(n == startNode){
33024                 return true;
33025             }
33026             if(af[n.id]){
33027                 return false;
33028             }
33029             var m = fn.call(scope || n, n);
33030             if(!m || rv){
33031                 af[n.id] = n;
33032                 n.ui.hide();
33033                 return false;
33034             }
33035             return true;
33036         };
33037         startNode.cascade(f);
33038         if(this.remove){
33039            for(var id in af){
33040                if(typeof id != "function"){
33041                    var n = af[id];
33042                    if(n && n.parentNode){
33043                        n.parentNode.removeChild(n);
33044                    }
33045                }
33046            }
33047         }
33048     },
33049
33050     /**
33051      * Clears the current filter. Note: with the "remove" option
33052      * set a filter cannot be cleared.
33053      */
33054     clear : function(){
33055         var t = this.tree;
33056         var af = this.filtered;
33057         for(var id in af){
33058             if(typeof id != "function"){
33059                 var n = af[id];
33060                 if(n){
33061                     n.ui.show();
33062                 }
33063             }
33064         }
33065         this.filtered = {};
33066     }
33067 };
33068 /*
33069  * Based on:
33070  * Ext JS Library 1.1.1
33071  * Copyright(c) 2006-2007, Ext JS, LLC.
33072  *
33073  * Originally Released Under LGPL - original licence link has changed is not relivant.
33074  *
33075  * Fork - LGPL
33076  * <script type="text/javascript">
33077  */
33078  
33079
33080 /**
33081  * @class Roo.tree.TreeSorter
33082  * Provides sorting of nodes in a TreePanel
33083  * 
33084  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33085  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33086  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33087  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33088  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33089  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33090  * @constructor
33091  * @param {TreePanel} tree
33092  * @param {Object} config
33093  */
33094 Roo.tree.TreeSorter = function(tree, config){
33095     Roo.apply(this, config);
33096     tree.on("beforechildrenrendered", this.doSort, this);
33097     tree.on("append", this.updateSort, this);
33098     tree.on("insert", this.updateSort, this);
33099     
33100     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33101     var p = this.property || "text";
33102     var sortType = this.sortType;
33103     var fs = this.folderSort;
33104     var cs = this.caseSensitive === true;
33105     var leafAttr = this.leafAttr || 'leaf';
33106
33107     this.sortFn = function(n1, n2){
33108         if(fs){
33109             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33110                 return 1;
33111             }
33112             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33113                 return -1;
33114             }
33115         }
33116         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33117         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33118         if(v1 < v2){
33119                         return dsc ? +1 : -1;
33120                 }else if(v1 > v2){
33121                         return dsc ? -1 : +1;
33122         }else{
33123                 return 0;
33124         }
33125     };
33126 };
33127
33128 Roo.tree.TreeSorter.prototype = {
33129     doSort : function(node){
33130         node.sort(this.sortFn);
33131     },
33132     
33133     compareNodes : function(n1, n2){
33134         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33135     },
33136     
33137     updateSort : function(tree, node){
33138         if(node.childrenRendered){
33139             this.doSort.defer(1, this, [node]);
33140         }
33141     }
33142 };/*
33143  * Based on:
33144  * Ext JS Library 1.1.1
33145  * Copyright(c) 2006-2007, Ext JS, LLC.
33146  *
33147  * Originally Released Under LGPL - original licence link has changed is not relivant.
33148  *
33149  * Fork - LGPL
33150  * <script type="text/javascript">
33151  */
33152
33153 if(Roo.dd.DropZone){
33154     
33155 Roo.tree.TreeDropZone = function(tree, config){
33156     this.allowParentInsert = false;
33157     this.allowContainerDrop = false;
33158     this.appendOnly = false;
33159     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33160     this.tree = tree;
33161     this.lastInsertClass = "x-tree-no-status";
33162     this.dragOverData = {};
33163 };
33164
33165 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33166     ddGroup : "TreeDD",
33167     
33168     expandDelay : 1000,
33169     
33170     expandNode : function(node){
33171         if(node.hasChildNodes() && !node.isExpanded()){
33172             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33173         }
33174     },
33175     
33176     queueExpand : function(node){
33177         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33178     },
33179     
33180     cancelExpand : function(){
33181         if(this.expandProcId){
33182             clearTimeout(this.expandProcId);
33183             this.expandProcId = false;
33184         }
33185     },
33186     
33187     isValidDropPoint : function(n, pt, dd, e, data){
33188         if(!n || !data){ return false; }
33189         var targetNode = n.node;
33190         var dropNode = data.node;
33191         // default drop rules
33192         if(!(targetNode && targetNode.isTarget && pt)){
33193             return false;
33194         }
33195         if(pt == "append" && targetNode.allowChildren === false){
33196             return false;
33197         }
33198         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33199             return false;
33200         }
33201         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33202             return false;
33203         }
33204         // reuse the object
33205         var overEvent = this.dragOverData;
33206         overEvent.tree = this.tree;
33207         overEvent.target = targetNode;
33208         overEvent.data = data;
33209         overEvent.point = pt;
33210         overEvent.source = dd;
33211         overEvent.rawEvent = e;
33212         overEvent.dropNode = dropNode;
33213         overEvent.cancel = false;  
33214         var result = this.tree.fireEvent("nodedragover", overEvent);
33215         return overEvent.cancel === false && result !== false;
33216     },
33217     
33218     getDropPoint : function(e, n, dd){
33219         var tn = n.node;
33220         if(tn.isRoot){
33221             return tn.allowChildren !== false ? "append" : false; // always append for root
33222         }
33223         var dragEl = n.ddel;
33224         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33225         var y = Roo.lib.Event.getPageY(e);
33226         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33227         
33228         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33229         var noAppend = tn.allowChildren === false;
33230         if(this.appendOnly || tn.parentNode.allowChildren === false){
33231             return noAppend ? false : "append";
33232         }
33233         var noBelow = false;
33234         if(!this.allowParentInsert){
33235             noBelow = tn.hasChildNodes() && tn.isExpanded();
33236         }
33237         var q = (b - t) / (noAppend ? 2 : 3);
33238         if(y >= t && y < (t + q)){
33239             return "above";
33240         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33241             return "below";
33242         }else{
33243             return "append";
33244         }
33245     },
33246     
33247     onNodeEnter : function(n, dd, e, data){
33248         this.cancelExpand();
33249     },
33250     
33251     onNodeOver : function(n, dd, e, data){
33252         var pt = this.getDropPoint(e, n, dd);
33253         var node = n.node;
33254         
33255         // auto node expand check
33256         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33257             this.queueExpand(node);
33258         }else if(pt != "append"){
33259             this.cancelExpand();
33260         }
33261         
33262         // set the insert point style on the target node
33263         var returnCls = this.dropNotAllowed;
33264         if(this.isValidDropPoint(n, pt, dd, e, data)){
33265            if(pt){
33266                var el = n.ddel;
33267                var cls;
33268                if(pt == "above"){
33269                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33270                    cls = "x-tree-drag-insert-above";
33271                }else if(pt == "below"){
33272                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33273                    cls = "x-tree-drag-insert-below";
33274                }else{
33275                    returnCls = "x-tree-drop-ok-append";
33276                    cls = "x-tree-drag-append";
33277                }
33278                if(this.lastInsertClass != cls){
33279                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33280                    this.lastInsertClass = cls;
33281                }
33282            }
33283        }
33284        return returnCls;
33285     },
33286     
33287     onNodeOut : function(n, dd, e, data){
33288         this.cancelExpand();
33289         this.removeDropIndicators(n);
33290     },
33291     
33292     onNodeDrop : function(n, dd, e, data){
33293         var point = this.getDropPoint(e, n, dd);
33294         var targetNode = n.node;
33295         targetNode.ui.startDrop();
33296         if(!this.isValidDropPoint(n, point, dd, e, data)){
33297             targetNode.ui.endDrop();
33298             return false;
33299         }
33300         // first try to find the drop node
33301         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33302         var dropEvent = {
33303             tree : this.tree,
33304             target: targetNode,
33305             data: data,
33306             point: point,
33307             source: dd,
33308             rawEvent: e,
33309             dropNode: dropNode,
33310             cancel: !dropNode   
33311         };
33312         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33313         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33314             targetNode.ui.endDrop();
33315             return false;
33316         }
33317         // allow target changing
33318         targetNode = dropEvent.target;
33319         if(point == "append" && !targetNode.isExpanded()){
33320             targetNode.expand(false, null, function(){
33321                 this.completeDrop(dropEvent);
33322             }.createDelegate(this));
33323         }else{
33324             this.completeDrop(dropEvent);
33325         }
33326         return true;
33327     },
33328     
33329     completeDrop : function(de){
33330         var ns = de.dropNode, p = de.point, t = de.target;
33331         if(!(ns instanceof Array)){
33332             ns = [ns];
33333         }
33334         var n;
33335         for(var i = 0, len = ns.length; i < len; i++){
33336             n = ns[i];
33337             if(p == "above"){
33338                 t.parentNode.insertBefore(n, t);
33339             }else if(p == "below"){
33340                 t.parentNode.insertBefore(n, t.nextSibling);
33341             }else{
33342                 t.appendChild(n);
33343             }
33344         }
33345         n.ui.focus();
33346         if(this.tree.hlDrop){
33347             n.ui.highlight();
33348         }
33349         t.ui.endDrop();
33350         this.tree.fireEvent("nodedrop", de);
33351     },
33352     
33353     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33354         if(this.tree.hlDrop){
33355             dropNode.ui.focus();
33356             dropNode.ui.highlight();
33357         }
33358         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33359     },
33360     
33361     getTree : function(){
33362         return this.tree;
33363     },
33364     
33365     removeDropIndicators : function(n){
33366         if(n && n.ddel){
33367             var el = n.ddel;
33368             Roo.fly(el).removeClass([
33369                     "x-tree-drag-insert-above",
33370                     "x-tree-drag-insert-below",
33371                     "x-tree-drag-append"]);
33372             this.lastInsertClass = "_noclass";
33373         }
33374     },
33375     
33376     beforeDragDrop : function(target, e, id){
33377         this.cancelExpand();
33378         return true;
33379     },
33380     
33381     afterRepair : function(data){
33382         if(data && Roo.enableFx){
33383             data.node.ui.highlight();
33384         }
33385         this.hideProxy();
33386     }    
33387 });
33388
33389 }
33390 /*
33391  * Based on:
33392  * Ext JS Library 1.1.1
33393  * Copyright(c) 2006-2007, Ext JS, LLC.
33394  *
33395  * Originally Released Under LGPL - original licence link has changed is not relivant.
33396  *
33397  * Fork - LGPL
33398  * <script type="text/javascript">
33399  */
33400  
33401
33402 if(Roo.dd.DragZone){
33403 Roo.tree.TreeDragZone = function(tree, config){
33404     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33405     this.tree = tree;
33406 };
33407
33408 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33409     ddGroup : "TreeDD",
33410     
33411     onBeforeDrag : function(data, e){
33412         var n = data.node;
33413         return n && n.draggable && !n.disabled;
33414     },
33415     
33416     onInitDrag : function(e){
33417         var data = this.dragData;
33418         this.tree.getSelectionModel().select(data.node);
33419         this.proxy.update("");
33420         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33421         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33422     },
33423     
33424     getRepairXY : function(e, data){
33425         return data.node.ui.getDDRepairXY();
33426     },
33427     
33428     onEndDrag : function(data, e){
33429         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33430     },
33431     
33432     onValidDrop : function(dd, e, id){
33433         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33434         this.hideProxy();
33435     },
33436     
33437     beforeInvalidDrop : function(e, id){
33438         // this scrolls the original position back into view
33439         var sm = this.tree.getSelectionModel();
33440         sm.clearSelections();
33441         sm.select(this.dragData.node);
33442     }
33443 });
33444 }/*
33445  * Based on:
33446  * Ext JS Library 1.1.1
33447  * Copyright(c) 2006-2007, Ext JS, LLC.
33448  *
33449  * Originally Released Under LGPL - original licence link has changed is not relivant.
33450  *
33451  * Fork - LGPL
33452  * <script type="text/javascript">
33453  */
33454 /**
33455  * @class Roo.tree.TreeEditor
33456  * @extends Roo.Editor
33457  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33458  * as the editor field.
33459  * @constructor
33460  * @param {TreePanel} tree
33461  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33462  */
33463 Roo.tree.TreeEditor = function(tree, config){
33464     config = config || {};
33465     var field = config.events ? config : new Roo.form.TextField(config);
33466     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33467
33468     this.tree = tree;
33469
33470     tree.on('beforeclick', this.beforeNodeClick, this);
33471     tree.getTreeEl().on('mousedown', this.hide, this);
33472     this.on('complete', this.updateNode, this);
33473     this.on('beforestartedit', this.fitToTree, this);
33474     this.on('startedit', this.bindScroll, this, {delay:10});
33475     this.on('specialkey', this.onSpecialKey, this);
33476 };
33477
33478 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33479     /**
33480      * @cfg {String} alignment
33481      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33482      */
33483     alignment: "l-l",
33484     // inherit
33485     autoSize: false,
33486     /**
33487      * @cfg {Boolean} hideEl
33488      * True to hide the bound element while the editor is displayed (defaults to false)
33489      */
33490     hideEl : false,
33491     /**
33492      * @cfg {String} cls
33493      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33494      */
33495     cls: "x-small-editor x-tree-editor",
33496     /**
33497      * @cfg {Boolean} shim
33498      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33499      */
33500     shim:false,
33501     // inherit
33502     shadow:"frame",
33503     /**
33504      * @cfg {Number} maxWidth
33505      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33506      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33507      * scroll and client offsets into account prior to each edit.
33508      */
33509     maxWidth: 250,
33510
33511     editDelay : 350,
33512
33513     // private
33514     fitToTree : function(ed, el){
33515         var td = this.tree.getTreeEl().dom, nd = el.dom;
33516         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33517             td.scrollLeft = nd.offsetLeft;
33518         }
33519         var w = Math.min(
33520                 this.maxWidth,
33521                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33522         this.setSize(w, '');
33523     },
33524
33525     // private
33526     triggerEdit : function(node){
33527         this.completeEdit();
33528         this.editNode = node;
33529         this.startEdit(node.ui.textNode, node.text);
33530     },
33531
33532     // private
33533     bindScroll : function(){
33534         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33535     },
33536
33537     // private
33538     beforeNodeClick : function(node, e){
33539         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33540         this.lastClick = new Date();
33541         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33542             e.stopEvent();
33543             this.triggerEdit(node);
33544             return false;
33545         }
33546     },
33547
33548     // private
33549     updateNode : function(ed, value){
33550         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33551         this.editNode.setText(value);
33552     },
33553
33554     // private
33555     onHide : function(){
33556         Roo.tree.TreeEditor.superclass.onHide.call(this);
33557         if(this.editNode){
33558             this.editNode.ui.focus();
33559         }
33560     },
33561
33562     // private
33563     onSpecialKey : function(field, e){
33564         var k = e.getKey();
33565         if(k == e.ESC){
33566             e.stopEvent();
33567             this.cancelEdit();
33568         }else if(k == e.ENTER && !e.hasModifier()){
33569             e.stopEvent();
33570             this.completeEdit();
33571         }
33572     }
33573 });//<Script type="text/javascript">
33574 /*
33575  * Based on:
33576  * Ext JS Library 1.1.1
33577  * Copyright(c) 2006-2007, Ext JS, LLC.
33578  *
33579  * Originally Released Under LGPL - original licence link has changed is not relivant.
33580  *
33581  * Fork - LGPL
33582  * <script type="text/javascript">
33583  */
33584  
33585 /**
33586  * Not documented??? - probably should be...
33587  */
33588
33589 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33590     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33591     
33592     renderElements : function(n, a, targetNode, bulkRender){
33593         //consel.log("renderElements?");
33594         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33595
33596         var t = n.getOwnerTree();
33597         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33598         
33599         var cols = t.columns;
33600         var bw = t.borderWidth;
33601         var c = cols[0];
33602         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33603          var cb = typeof a.checked == "boolean";
33604         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33605         var colcls = 'x-t-' + tid + '-c0';
33606         var buf = [
33607             '<li class="x-tree-node">',
33608             
33609                 
33610                 '<div class="x-tree-node-el ', a.cls,'">',
33611                     // extran...
33612                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33613                 
33614                 
33615                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33616                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33617                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33618                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33619                            (a.iconCls ? ' '+a.iconCls : ''),
33620                            '" unselectable="on" />',
33621                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33622                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33623                              
33624                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33625                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33626                             '<span unselectable="on" qtip="' + tx + '">',
33627                              tx,
33628                              '</span></a>' ,
33629                     '</div>',
33630                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33631                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33632                  ];
33633         for(var i = 1, len = cols.length; i < len; i++){
33634             c = cols[i];
33635             colcls = 'x-t-' + tid + '-c' +i;
33636             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33637             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33638                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33639                       "</div>");
33640          }
33641          
33642          buf.push(
33643             '</a>',
33644             '<div class="x-clear"></div></div>',
33645             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33646             "</li>");
33647         
33648         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33649             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33650                                 n.nextSibling.ui.getEl(), buf.join(""));
33651         }else{
33652             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33653         }
33654         var el = this.wrap.firstChild;
33655         this.elRow = el;
33656         this.elNode = el.firstChild;
33657         this.ranchor = el.childNodes[1];
33658         this.ctNode = this.wrap.childNodes[1];
33659         var cs = el.firstChild.childNodes;
33660         this.indentNode = cs[0];
33661         this.ecNode = cs[1];
33662         this.iconNode = cs[2];
33663         var index = 3;
33664         if(cb){
33665             this.checkbox = cs[3];
33666             index++;
33667         }
33668         this.anchor = cs[index];
33669         
33670         this.textNode = cs[index].firstChild;
33671         
33672         //el.on("click", this.onClick, this);
33673         //el.on("dblclick", this.onDblClick, this);
33674         
33675         
33676        // console.log(this);
33677     },
33678     initEvents : function(){
33679         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33680         
33681             
33682         var a = this.ranchor;
33683
33684         var el = Roo.get(a);
33685
33686         if(Roo.isOpera){ // opera render bug ignores the CSS
33687             el.setStyle("text-decoration", "none");
33688         }
33689
33690         el.on("click", this.onClick, this);
33691         el.on("dblclick", this.onDblClick, this);
33692         el.on("contextmenu", this.onContextMenu, this);
33693         
33694     },
33695     
33696     /*onSelectedChange : function(state){
33697         if(state){
33698             this.focus();
33699             this.addClass("x-tree-selected");
33700         }else{
33701             //this.blur();
33702             this.removeClass("x-tree-selected");
33703         }
33704     },*/
33705     addClass : function(cls){
33706         if(this.elRow){
33707             Roo.fly(this.elRow).addClass(cls);
33708         }
33709         
33710     },
33711     
33712     
33713     removeClass : function(cls){
33714         if(this.elRow){
33715             Roo.fly(this.elRow).removeClass(cls);
33716         }
33717     }
33718
33719     
33720     
33721 });//<Script type="text/javascript">
33722
33723 /*
33724  * Based on:
33725  * Ext JS Library 1.1.1
33726  * Copyright(c) 2006-2007, Ext JS, LLC.
33727  *
33728  * Originally Released Under LGPL - original licence link has changed is not relivant.
33729  *
33730  * Fork - LGPL
33731  * <script type="text/javascript">
33732  */
33733  
33734
33735 /**
33736  * @class Roo.tree.ColumnTree
33737  * @extends Roo.data.TreePanel
33738  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33739  * @cfg {int} borderWidth  compined right/left border allowance
33740  * @constructor
33741  * @param {String/HTMLElement/Element} el The container element
33742  * @param {Object} config
33743  */
33744 Roo.tree.ColumnTree =  function(el, config)
33745 {
33746    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33747    this.addEvents({
33748         /**
33749         * @event resize
33750         * Fire this event on a container when it resizes
33751         * @param {int} w Width
33752         * @param {int} h Height
33753         */
33754        "resize" : true
33755     });
33756     this.on('resize', this.onResize, this);
33757 };
33758
33759 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33760     //lines:false,
33761     
33762     
33763     borderWidth: Roo.isBorderBox ? 0 : 2, 
33764     headEls : false,
33765     
33766     render : function(){
33767         // add the header.....
33768        
33769         Roo.tree.ColumnTree.superclass.render.apply(this);
33770         
33771         this.el.addClass('x-column-tree');
33772         
33773         this.headers = this.el.createChild(
33774             {cls:'x-tree-headers'},this.innerCt.dom);
33775    
33776         var cols = this.columns, c;
33777         var totalWidth = 0;
33778         this.headEls = [];
33779         var  len = cols.length;
33780         for(var i = 0; i < len; i++){
33781              c = cols[i];
33782              totalWidth += c.width;
33783             this.headEls.push(this.headers.createChild({
33784                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33785                  cn: {
33786                      cls:'x-tree-hd-text',
33787                      html: c.header
33788                  },
33789                  style:'width:'+(c.width-this.borderWidth)+'px;'
33790              }));
33791         }
33792         this.headers.createChild({cls:'x-clear'});
33793         // prevent floats from wrapping when clipped
33794         this.headers.setWidth(totalWidth);
33795         //this.innerCt.setWidth(totalWidth);
33796         this.innerCt.setStyle({ overflow: 'auto' });
33797         this.onResize(this.width, this.height);
33798              
33799         
33800     },
33801     onResize : function(w,h)
33802     {
33803         this.height = h;
33804         this.width = w;
33805         // resize cols..
33806         this.innerCt.setWidth(this.width);
33807         this.innerCt.setHeight(this.height-20);
33808         
33809         // headers...
33810         var cols = this.columns, c;
33811         var totalWidth = 0;
33812         var expEl = false;
33813         var len = cols.length;
33814         for(var i = 0; i < len; i++){
33815             c = cols[i];
33816             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33817                 // it's the expander..
33818                 expEl  = this.headEls[i];
33819                 continue;
33820             }
33821             totalWidth += c.width;
33822             
33823         }
33824         if (expEl) {
33825             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33826         }
33827         this.headers.setWidth(w-20);
33828
33829         
33830         
33831         
33832     }
33833 });
33834 /*
33835  * Based on:
33836  * Ext JS Library 1.1.1
33837  * Copyright(c) 2006-2007, Ext JS, LLC.
33838  *
33839  * Originally Released Under LGPL - original licence link has changed is not relivant.
33840  *
33841  * Fork - LGPL
33842  * <script type="text/javascript">
33843  */
33844  
33845 /**
33846  * @class Roo.menu.Menu
33847  * @extends Roo.util.Observable
33848  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33849  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33850  * @constructor
33851  * Creates a new Menu
33852  * @param {Object} config Configuration options
33853  */
33854 Roo.menu.Menu = function(config){
33855     Roo.apply(this, config);
33856     this.id = this.id || Roo.id();
33857     this.addEvents({
33858         /**
33859          * @event beforeshow
33860          * Fires before this menu is displayed
33861          * @param {Roo.menu.Menu} this
33862          */
33863         beforeshow : true,
33864         /**
33865          * @event beforehide
33866          * Fires before this menu is hidden
33867          * @param {Roo.menu.Menu} this
33868          */
33869         beforehide : true,
33870         /**
33871          * @event show
33872          * Fires after this menu is displayed
33873          * @param {Roo.menu.Menu} this
33874          */
33875         show : true,
33876         /**
33877          * @event hide
33878          * Fires after this menu is hidden
33879          * @param {Roo.menu.Menu} this
33880          */
33881         hide : true,
33882         /**
33883          * @event click
33884          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33885          * @param {Roo.menu.Menu} this
33886          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33887          * @param {Roo.EventObject} e
33888          */
33889         click : true,
33890         /**
33891          * @event mouseover
33892          * Fires when the mouse is hovering over this menu
33893          * @param {Roo.menu.Menu} this
33894          * @param {Roo.EventObject} e
33895          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33896          */
33897         mouseover : true,
33898         /**
33899          * @event mouseout
33900          * Fires when the mouse exits this menu
33901          * @param {Roo.menu.Menu} this
33902          * @param {Roo.EventObject} e
33903          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33904          */
33905         mouseout : true,
33906         /**
33907          * @event itemclick
33908          * Fires when a menu item contained in this menu is clicked
33909          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33910          * @param {Roo.EventObject} e
33911          */
33912         itemclick: true
33913     });
33914     if (this.registerMenu) {
33915         Roo.menu.MenuMgr.register(this);
33916     }
33917     
33918     var mis = this.items;
33919     this.items = new Roo.util.MixedCollection();
33920     if(mis){
33921         this.add.apply(this, mis);
33922     }
33923 };
33924
33925 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33926     /**
33927      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33928      */
33929     minWidth : 120,
33930     /**
33931      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33932      * for bottom-right shadow (defaults to "sides")
33933      */
33934     shadow : "sides",
33935     /**
33936      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33937      * this menu (defaults to "tl-tr?")
33938      */
33939     subMenuAlign : "tl-tr?",
33940     /**
33941      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33942      * relative to its element of origin (defaults to "tl-bl?")
33943      */
33944     defaultAlign : "tl-bl?",
33945     /**
33946      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33947      */
33948     allowOtherMenus : false,
33949     /**
33950      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33951      */
33952     registerMenu : true,
33953
33954     hidden:true,
33955
33956     // private
33957     render : function(){
33958         if(this.el){
33959             return;
33960         }
33961         var el = this.el = new Roo.Layer({
33962             cls: "x-menu",
33963             shadow:this.shadow,
33964             constrain: false,
33965             parentEl: this.parentEl || document.body,
33966             zindex:15000
33967         });
33968
33969         this.keyNav = new Roo.menu.MenuNav(this);
33970
33971         if(this.plain){
33972             el.addClass("x-menu-plain");
33973         }
33974         if(this.cls){
33975             el.addClass(this.cls);
33976         }
33977         // generic focus element
33978         this.focusEl = el.createChild({
33979             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33980         });
33981         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33982         ul.on("click", this.onClick, this);
33983         ul.on("mouseover", this.onMouseOver, this);
33984         ul.on("mouseout", this.onMouseOut, this);
33985         this.items.each(function(item){
33986             var li = document.createElement("li");
33987             li.className = "x-menu-list-item";
33988             ul.dom.appendChild(li);
33989             item.render(li, this);
33990         }, this);
33991         this.ul = ul;
33992         this.autoWidth();
33993     },
33994
33995     // private
33996     autoWidth : function(){
33997         var el = this.el, ul = this.ul;
33998         if(!el){
33999             return;
34000         }
34001         var w = this.width;
34002         if(w){
34003             el.setWidth(w);
34004         }else if(Roo.isIE){
34005             el.setWidth(this.minWidth);
34006             var t = el.dom.offsetWidth; // force recalc
34007             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34008         }
34009     },
34010
34011     // private
34012     delayAutoWidth : function(){
34013         if(this.rendered){
34014             if(!this.awTask){
34015                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34016             }
34017             this.awTask.delay(20);
34018         }
34019     },
34020
34021     // private
34022     findTargetItem : function(e){
34023         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34024         if(t && t.menuItemId){
34025             return this.items.get(t.menuItemId);
34026         }
34027     },
34028
34029     // private
34030     onClick : function(e){
34031         var t;
34032         if(t = this.findTargetItem(e)){
34033             t.onClick(e);
34034             this.fireEvent("click", this, t, e);
34035         }
34036     },
34037
34038     // private
34039     setActiveItem : function(item, autoExpand){
34040         if(item != this.activeItem){
34041             if(this.activeItem){
34042                 this.activeItem.deactivate();
34043             }
34044             this.activeItem = item;
34045             item.activate(autoExpand);
34046         }else if(autoExpand){
34047             item.expandMenu();
34048         }
34049     },
34050
34051     // private
34052     tryActivate : function(start, step){
34053         var items = this.items;
34054         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34055             var item = items.get(i);
34056             if(!item.disabled && item.canActivate){
34057                 this.setActiveItem(item, false);
34058                 return item;
34059             }
34060         }
34061         return false;
34062     },
34063
34064     // private
34065     onMouseOver : function(e){
34066         var t;
34067         if(t = this.findTargetItem(e)){
34068             if(t.canActivate && !t.disabled){
34069                 this.setActiveItem(t, true);
34070             }
34071         }
34072         this.fireEvent("mouseover", this, e, t);
34073     },
34074
34075     // private
34076     onMouseOut : function(e){
34077         var t;
34078         if(t = this.findTargetItem(e)){
34079             if(t == this.activeItem && t.shouldDeactivate(e)){
34080                 this.activeItem.deactivate();
34081                 delete this.activeItem;
34082             }
34083         }
34084         this.fireEvent("mouseout", this, e, t);
34085     },
34086
34087     /**
34088      * Read-only.  Returns true if the menu is currently displayed, else false.
34089      * @type Boolean
34090      */
34091     isVisible : function(){
34092         return this.el && !this.hidden;
34093     },
34094
34095     /**
34096      * Displays this menu relative to another element
34097      * @param {String/HTMLElement/Roo.Element} element The element to align to
34098      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34099      * the element (defaults to this.defaultAlign)
34100      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34101      */
34102     show : function(el, pos, parentMenu){
34103         this.parentMenu = parentMenu;
34104         if(!this.el){
34105             this.render();
34106         }
34107         this.fireEvent("beforeshow", this);
34108         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34109     },
34110
34111     /**
34112      * Displays this menu at a specific xy position
34113      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34114      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34115      */
34116     showAt : function(xy, parentMenu, /* private: */_e){
34117         this.parentMenu = parentMenu;
34118         if(!this.el){
34119             this.render();
34120         }
34121         if(_e !== false){
34122             this.fireEvent("beforeshow", this);
34123             xy = this.el.adjustForConstraints(xy);
34124         }
34125         this.el.setXY(xy);
34126         this.el.show();
34127         this.hidden = false;
34128         this.focus();
34129         this.fireEvent("show", this);
34130     },
34131
34132     focus : function(){
34133         if(!this.hidden){
34134             this.doFocus.defer(50, this);
34135         }
34136     },
34137
34138     doFocus : function(){
34139         if(!this.hidden){
34140             this.focusEl.focus();
34141         }
34142     },
34143
34144     /**
34145      * Hides this menu and optionally all parent menus
34146      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34147      */
34148     hide : function(deep){
34149         if(this.el && this.isVisible()){
34150             this.fireEvent("beforehide", this);
34151             if(this.activeItem){
34152                 this.activeItem.deactivate();
34153                 this.activeItem = null;
34154             }
34155             this.el.hide();
34156             this.hidden = true;
34157             this.fireEvent("hide", this);
34158         }
34159         if(deep === true && this.parentMenu){
34160             this.parentMenu.hide(true);
34161         }
34162     },
34163
34164     /**
34165      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34166      * Any of the following are valid:
34167      * <ul>
34168      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34169      * <li>An HTMLElement object which will be converted to a menu item</li>
34170      * <li>A menu item config object that will be created as a new menu item</li>
34171      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34172      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34173      * </ul>
34174      * Usage:
34175      * <pre><code>
34176 // Create the menu
34177 var menu = new Roo.menu.Menu();
34178
34179 // Create a menu item to add by reference
34180 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34181
34182 // Add a bunch of items at once using different methods.
34183 // Only the last item added will be returned.
34184 var item = menu.add(
34185     menuItem,                // add existing item by ref
34186     'Dynamic Item',          // new TextItem
34187     '-',                     // new separator
34188     { text: 'Config Item' }  // new item by config
34189 );
34190 </code></pre>
34191      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34192      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34193      */
34194     add : function(){
34195         var a = arguments, l = a.length, item;
34196         for(var i = 0; i < l; i++){
34197             var el = a[i];
34198             if ((typeof(el) == "object") && el.xtype && el.xns) {
34199                 el = Roo.factory(el, Roo.menu);
34200             }
34201             
34202             if(el.render){ // some kind of Item
34203                 item = this.addItem(el);
34204             }else if(typeof el == "string"){ // string
34205                 if(el == "separator" || el == "-"){
34206                     item = this.addSeparator();
34207                 }else{
34208                     item = this.addText(el);
34209                 }
34210             }else if(el.tagName || el.el){ // element
34211                 item = this.addElement(el);
34212             }else if(typeof el == "object"){ // must be menu item config?
34213                 item = this.addMenuItem(el);
34214             }
34215         }
34216         return item;
34217     },
34218
34219     /**
34220      * Returns this menu's underlying {@link Roo.Element} object
34221      * @return {Roo.Element} The element
34222      */
34223     getEl : function(){
34224         if(!this.el){
34225             this.render();
34226         }
34227         return this.el;
34228     },
34229
34230     /**
34231      * Adds a separator bar to the menu
34232      * @return {Roo.menu.Item} The menu item that was added
34233      */
34234     addSeparator : function(){
34235         return this.addItem(new Roo.menu.Separator());
34236     },
34237
34238     /**
34239      * Adds an {@link Roo.Element} object to the menu
34240      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34241      * @return {Roo.menu.Item} The menu item that was added
34242      */
34243     addElement : function(el){
34244         return this.addItem(new Roo.menu.BaseItem(el));
34245     },
34246
34247     /**
34248      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34249      * @param {Roo.menu.Item} item The menu item to add
34250      * @return {Roo.menu.Item} The menu item that was added
34251      */
34252     addItem : function(item){
34253         this.items.add(item);
34254         if(this.ul){
34255             var li = document.createElement("li");
34256             li.className = "x-menu-list-item";
34257             this.ul.dom.appendChild(li);
34258             item.render(li, this);
34259             this.delayAutoWidth();
34260         }
34261         return item;
34262     },
34263
34264     /**
34265      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34266      * @param {Object} config A MenuItem config object
34267      * @return {Roo.menu.Item} The menu item that was added
34268      */
34269     addMenuItem : function(config){
34270         if(!(config instanceof Roo.menu.Item)){
34271             if(typeof config.checked == "boolean"){ // must be check menu item config?
34272                 config = new Roo.menu.CheckItem(config);
34273             }else{
34274                 config = new Roo.menu.Item(config);
34275             }
34276         }
34277         return this.addItem(config);
34278     },
34279
34280     /**
34281      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34282      * @param {String} text The text to display in the menu item
34283      * @return {Roo.menu.Item} The menu item that was added
34284      */
34285     addText : function(text){
34286         return this.addItem(new Roo.menu.TextItem({ text : text }));
34287     },
34288
34289     /**
34290      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34291      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34292      * @param {Roo.menu.Item} item The menu item to add
34293      * @return {Roo.menu.Item} The menu item that was added
34294      */
34295     insert : function(index, item){
34296         this.items.insert(index, item);
34297         if(this.ul){
34298             var li = document.createElement("li");
34299             li.className = "x-menu-list-item";
34300             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34301             item.render(li, this);
34302             this.delayAutoWidth();
34303         }
34304         return item;
34305     },
34306
34307     /**
34308      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34309      * @param {Roo.menu.Item} item The menu item to remove
34310      */
34311     remove : function(item){
34312         this.items.removeKey(item.id);
34313         item.destroy();
34314     },
34315
34316     /**
34317      * Removes and destroys all items in the menu
34318      */
34319     removeAll : function(){
34320         var f;
34321         while(f = this.items.first()){
34322             this.remove(f);
34323         }
34324     }
34325 });
34326
34327 // MenuNav is a private utility class used internally by the Menu
34328 Roo.menu.MenuNav = function(menu){
34329     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34330     this.scope = this.menu = menu;
34331 };
34332
34333 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34334     doRelay : function(e, h){
34335         var k = e.getKey();
34336         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34337             this.menu.tryActivate(0, 1);
34338             return false;
34339         }
34340         return h.call(this.scope || this, e, this.menu);
34341     },
34342
34343     up : function(e, m){
34344         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34345             m.tryActivate(m.items.length-1, -1);
34346         }
34347     },
34348
34349     down : function(e, m){
34350         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34351             m.tryActivate(0, 1);
34352         }
34353     },
34354
34355     right : function(e, m){
34356         if(m.activeItem){
34357             m.activeItem.expandMenu(true);
34358         }
34359     },
34360
34361     left : function(e, m){
34362         m.hide();
34363         if(m.parentMenu && m.parentMenu.activeItem){
34364             m.parentMenu.activeItem.activate();
34365         }
34366     },
34367
34368     enter : function(e, m){
34369         if(m.activeItem){
34370             e.stopPropagation();
34371             m.activeItem.onClick(e);
34372             m.fireEvent("click", this, m.activeItem);
34373             return true;
34374         }
34375     }
34376 });/*
34377  * Based on:
34378  * Ext JS Library 1.1.1
34379  * Copyright(c) 2006-2007, Ext JS, LLC.
34380  *
34381  * Originally Released Under LGPL - original licence link has changed is not relivant.
34382  *
34383  * Fork - LGPL
34384  * <script type="text/javascript">
34385  */
34386  
34387 /**
34388  * @class Roo.menu.MenuMgr
34389  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34390  * @singleton
34391  */
34392 Roo.menu.MenuMgr = function(){
34393    var menus, active, groups = {}, attached = false, lastShow = new Date();
34394
34395    // private - called when first menu is created
34396    function init(){
34397        menus = {};
34398        active = new Roo.util.MixedCollection();
34399        Roo.get(document).addKeyListener(27, function(){
34400            if(active.length > 0){
34401                hideAll();
34402            }
34403        });
34404    }
34405
34406    // private
34407    function hideAll(){
34408        if(active && active.length > 0){
34409            var c = active.clone();
34410            c.each(function(m){
34411                m.hide();
34412            });
34413        }
34414    }
34415
34416    // private
34417    function onHide(m){
34418        active.remove(m);
34419        if(active.length < 1){
34420            Roo.get(document).un("mousedown", onMouseDown);
34421            attached = false;
34422        }
34423    }
34424
34425    // private
34426    function onShow(m){
34427        var last = active.last();
34428        lastShow = new Date();
34429        active.add(m);
34430        if(!attached){
34431            Roo.get(document).on("mousedown", onMouseDown);
34432            attached = true;
34433        }
34434        if(m.parentMenu){
34435           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34436           m.parentMenu.activeChild = m;
34437        }else if(last && last.isVisible()){
34438           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34439        }
34440    }
34441
34442    // private
34443    function onBeforeHide(m){
34444        if(m.activeChild){
34445            m.activeChild.hide();
34446        }
34447        if(m.autoHideTimer){
34448            clearTimeout(m.autoHideTimer);
34449            delete m.autoHideTimer;
34450        }
34451    }
34452
34453    // private
34454    function onBeforeShow(m){
34455        var pm = m.parentMenu;
34456        if(!pm && !m.allowOtherMenus){
34457            hideAll();
34458        }else if(pm && pm.activeChild && active != m){
34459            pm.activeChild.hide();
34460        }
34461    }
34462
34463    // private
34464    function onMouseDown(e){
34465        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34466            hideAll();
34467        }
34468    }
34469
34470    // private
34471    function onBeforeCheck(mi, state){
34472        if(state){
34473            var g = groups[mi.group];
34474            for(var i = 0, l = g.length; i < l; i++){
34475                if(g[i] != mi){
34476                    g[i].setChecked(false);
34477                }
34478            }
34479        }
34480    }
34481
34482    return {
34483
34484        /**
34485         * Hides all menus that are currently visible
34486         */
34487        hideAll : function(){
34488             hideAll();  
34489        },
34490
34491        // private
34492        register : function(menu){
34493            if(!menus){
34494                init();
34495            }
34496            menus[menu.id] = menu;
34497            menu.on("beforehide", onBeforeHide);
34498            menu.on("hide", onHide);
34499            menu.on("beforeshow", onBeforeShow);
34500            menu.on("show", onShow);
34501            var g = menu.group;
34502            if(g && menu.events["checkchange"]){
34503                if(!groups[g]){
34504                    groups[g] = [];
34505                }
34506                groups[g].push(menu);
34507                menu.on("checkchange", onCheck);
34508            }
34509        },
34510
34511         /**
34512          * Returns a {@link Roo.menu.Menu} object
34513          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34514          * be used to generate and return a new Menu instance.
34515          */
34516        get : function(menu){
34517            if(typeof menu == "string"){ // menu id
34518                return menus[menu];
34519            }else if(menu.events){  // menu instance
34520                return menu;
34521            }else if(typeof menu.length == 'number'){ // array of menu items?
34522                return new Roo.menu.Menu({items:menu});
34523            }else{ // otherwise, must be a config
34524                return new Roo.menu.Menu(menu);
34525            }
34526        },
34527
34528        // private
34529        unregister : function(menu){
34530            delete menus[menu.id];
34531            menu.un("beforehide", onBeforeHide);
34532            menu.un("hide", onHide);
34533            menu.un("beforeshow", onBeforeShow);
34534            menu.un("show", onShow);
34535            var g = menu.group;
34536            if(g && menu.events["checkchange"]){
34537                groups[g].remove(menu);
34538                menu.un("checkchange", onCheck);
34539            }
34540        },
34541
34542        // private
34543        registerCheckable : function(menuItem){
34544            var g = menuItem.group;
34545            if(g){
34546                if(!groups[g]){
34547                    groups[g] = [];
34548                }
34549                groups[g].push(menuItem);
34550                menuItem.on("beforecheckchange", onBeforeCheck);
34551            }
34552        },
34553
34554        // private
34555        unregisterCheckable : function(menuItem){
34556            var g = menuItem.group;
34557            if(g){
34558                groups[g].remove(menuItem);
34559                menuItem.un("beforecheckchange", onBeforeCheck);
34560            }
34561        }
34562    };
34563 }();/*
34564  * Based on:
34565  * Ext JS Library 1.1.1
34566  * Copyright(c) 2006-2007, Ext JS, LLC.
34567  *
34568  * Originally Released Under LGPL - original licence link has changed is not relivant.
34569  *
34570  * Fork - LGPL
34571  * <script type="text/javascript">
34572  */
34573  
34574
34575 /**
34576  * @class Roo.menu.BaseItem
34577  * @extends Roo.Component
34578  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34579  * management and base configuration options shared by all menu components.
34580  * @constructor
34581  * Creates a new BaseItem
34582  * @param {Object} config Configuration options
34583  */
34584 Roo.menu.BaseItem = function(config){
34585     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34586
34587     this.addEvents({
34588         /**
34589          * @event click
34590          * Fires when this item is clicked
34591          * @param {Roo.menu.BaseItem} this
34592          * @param {Roo.EventObject} e
34593          */
34594         click: true,
34595         /**
34596          * @event activate
34597          * Fires when this item is activated
34598          * @param {Roo.menu.BaseItem} this
34599          */
34600         activate : true,
34601         /**
34602          * @event deactivate
34603          * Fires when this item is deactivated
34604          * @param {Roo.menu.BaseItem} this
34605          */
34606         deactivate : true
34607     });
34608
34609     if(this.handler){
34610         this.on("click", this.handler, this.scope, true);
34611     }
34612 };
34613
34614 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34615     /**
34616      * @cfg {Function} handler
34617      * A function that will handle the click event of this menu item (defaults to undefined)
34618      */
34619     /**
34620      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34621      */
34622     canActivate : false,
34623     /**
34624      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34625      */
34626     activeClass : "x-menu-item-active",
34627     /**
34628      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34629      */
34630     hideOnClick : true,
34631     /**
34632      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34633      */
34634     hideDelay : 100,
34635
34636     // private
34637     ctype: "Roo.menu.BaseItem",
34638
34639     // private
34640     actionMode : "container",
34641
34642     // private
34643     render : function(container, parentMenu){
34644         this.parentMenu = parentMenu;
34645         Roo.menu.BaseItem.superclass.render.call(this, container);
34646         this.container.menuItemId = this.id;
34647     },
34648
34649     // private
34650     onRender : function(container, position){
34651         this.el = Roo.get(this.el);
34652         container.dom.appendChild(this.el.dom);
34653     },
34654
34655     // private
34656     onClick : function(e){
34657         if(!this.disabled && this.fireEvent("click", this, e) !== false
34658                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34659             this.handleClick(e);
34660         }else{
34661             e.stopEvent();
34662         }
34663     },
34664
34665     // private
34666     activate : function(){
34667         if(this.disabled){
34668             return false;
34669         }
34670         var li = this.container;
34671         li.addClass(this.activeClass);
34672         this.region = li.getRegion().adjust(2, 2, -2, -2);
34673         this.fireEvent("activate", this);
34674         return true;
34675     },
34676
34677     // private
34678     deactivate : function(){
34679         this.container.removeClass(this.activeClass);
34680         this.fireEvent("deactivate", this);
34681     },
34682
34683     // private
34684     shouldDeactivate : function(e){
34685         return !this.region || !this.region.contains(e.getPoint());
34686     },
34687
34688     // private
34689     handleClick : function(e){
34690         if(this.hideOnClick){
34691             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34692         }
34693     },
34694
34695     // private
34696     expandMenu : function(autoActivate){
34697         // do nothing
34698     },
34699
34700     // private
34701     hideMenu : function(){
34702         // do nothing
34703     }
34704 });/*
34705  * Based on:
34706  * Ext JS Library 1.1.1
34707  * Copyright(c) 2006-2007, Ext JS, LLC.
34708  *
34709  * Originally Released Under LGPL - original licence link has changed is not relivant.
34710  *
34711  * Fork - LGPL
34712  * <script type="text/javascript">
34713  */
34714  
34715 /**
34716  * @class Roo.menu.Adapter
34717  * @extends Roo.menu.BaseItem
34718  * 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.
34719  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34720  * @constructor
34721  * Creates a new Adapter
34722  * @param {Object} config Configuration options
34723  */
34724 Roo.menu.Adapter = function(component, config){
34725     Roo.menu.Adapter.superclass.constructor.call(this, config);
34726     this.component = component;
34727 };
34728 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34729     // private
34730     canActivate : true,
34731
34732     // private
34733     onRender : function(container, position){
34734         this.component.render(container);
34735         this.el = this.component.getEl();
34736     },
34737
34738     // private
34739     activate : function(){
34740         if(this.disabled){
34741             return false;
34742         }
34743         this.component.focus();
34744         this.fireEvent("activate", this);
34745         return true;
34746     },
34747
34748     // private
34749     deactivate : function(){
34750         this.fireEvent("deactivate", this);
34751     },
34752
34753     // private
34754     disable : function(){
34755         this.component.disable();
34756         Roo.menu.Adapter.superclass.disable.call(this);
34757     },
34758
34759     // private
34760     enable : function(){
34761         this.component.enable();
34762         Roo.menu.Adapter.superclass.enable.call(this);
34763     }
34764 });/*
34765  * Based on:
34766  * Ext JS Library 1.1.1
34767  * Copyright(c) 2006-2007, Ext JS, LLC.
34768  *
34769  * Originally Released Under LGPL - original licence link has changed is not relivant.
34770  *
34771  * Fork - LGPL
34772  * <script type="text/javascript">
34773  */
34774
34775 /**
34776  * @class Roo.menu.TextItem
34777  * @extends Roo.menu.BaseItem
34778  * Adds a static text string to a menu, usually used as either a heading or group separator.
34779  * Note: old style constructor with text is still supported.
34780  * 
34781  * @constructor
34782  * Creates a new TextItem
34783  * @param {Object} cfg Configuration
34784  */
34785 Roo.menu.TextItem = function(cfg){
34786     if (typeof(cfg) == 'string') {
34787         this.text = cfg;
34788     } else {
34789         Roo.apply(this,cfg);
34790     }
34791     
34792     Roo.menu.TextItem.superclass.constructor.call(this);
34793 };
34794
34795 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34796     /**
34797      * @cfg {Boolean} text Text to show on item.
34798      */
34799     text : '',
34800     
34801     /**
34802      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34803      */
34804     hideOnClick : false,
34805     /**
34806      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34807      */
34808     itemCls : "x-menu-text",
34809
34810     // private
34811     onRender : function(){
34812         var s = document.createElement("span");
34813         s.className = this.itemCls;
34814         s.innerHTML = this.text;
34815         this.el = s;
34816         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34817     }
34818 });/*
34819  * Based on:
34820  * Ext JS Library 1.1.1
34821  * Copyright(c) 2006-2007, Ext JS, LLC.
34822  *
34823  * Originally Released Under LGPL - original licence link has changed is not relivant.
34824  *
34825  * Fork - LGPL
34826  * <script type="text/javascript">
34827  */
34828
34829 /**
34830  * @class Roo.menu.Separator
34831  * @extends Roo.menu.BaseItem
34832  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34833  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34834  * @constructor
34835  * @param {Object} config Configuration options
34836  */
34837 Roo.menu.Separator = function(config){
34838     Roo.menu.Separator.superclass.constructor.call(this, config);
34839 };
34840
34841 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34842     /**
34843      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34844      */
34845     itemCls : "x-menu-sep",
34846     /**
34847      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34848      */
34849     hideOnClick : false,
34850
34851     // private
34852     onRender : function(li){
34853         var s = document.createElement("span");
34854         s.className = this.itemCls;
34855         s.innerHTML = "&#160;";
34856         this.el = s;
34857         li.addClass("x-menu-sep-li");
34858         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34859     }
34860 });/*
34861  * Based on:
34862  * Ext JS Library 1.1.1
34863  * Copyright(c) 2006-2007, Ext JS, LLC.
34864  *
34865  * Originally Released Under LGPL - original licence link has changed is not relivant.
34866  *
34867  * Fork - LGPL
34868  * <script type="text/javascript">
34869  */
34870 /**
34871  * @class Roo.menu.Item
34872  * @extends Roo.menu.BaseItem
34873  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34874  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34875  * activation and click handling.
34876  * @constructor
34877  * Creates a new Item
34878  * @param {Object} config Configuration options
34879  */
34880 Roo.menu.Item = function(config){
34881     Roo.menu.Item.superclass.constructor.call(this, config);
34882     if(this.menu){
34883         this.menu = Roo.menu.MenuMgr.get(this.menu);
34884     }
34885 };
34886 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34887     
34888     /**
34889      * @cfg {String} text
34890      * The text to show on the menu item.
34891      */
34892     text: '',
34893      /**
34894      * @cfg {String} HTML to render in menu
34895      * The text to show on the menu item (HTML version).
34896      */
34897     html: '',
34898     /**
34899      * @cfg {String} icon
34900      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34901      */
34902     icon: undefined,
34903     /**
34904      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34905      */
34906     itemCls : "x-menu-item",
34907     /**
34908      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34909      */
34910     canActivate : true,
34911     /**
34912      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34913      */
34914     showDelay: 200,
34915     // doc'd in BaseItem
34916     hideDelay: 200,
34917
34918     // private
34919     ctype: "Roo.menu.Item",
34920     
34921     // private
34922     onRender : function(container, position){
34923         var el = document.createElement("a");
34924         el.hideFocus = true;
34925         el.unselectable = "on";
34926         el.href = this.href || "#";
34927         if(this.hrefTarget){
34928             el.target = this.hrefTarget;
34929         }
34930         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34931         
34932         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34933         
34934         el.innerHTML = String.format(
34935                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34936                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34937         this.el = el;
34938         Roo.menu.Item.superclass.onRender.call(this, container, position);
34939     },
34940
34941     /**
34942      * Sets the text to display in this menu item
34943      * @param {String} text The text to display
34944      * @param {Boolean} isHTML true to indicate text is pure html.
34945      */
34946     setText : function(text, isHTML){
34947         if (isHTML) {
34948             this.html = text;
34949         } else {
34950             this.text = text;
34951             this.html = '';
34952         }
34953         if(this.rendered){
34954             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34955      
34956             this.el.update(String.format(
34957                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34958                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34959             this.parentMenu.autoWidth();
34960         }
34961     },
34962
34963     // private
34964     handleClick : function(e){
34965         if(!this.href){ // if no link defined, stop the event automatically
34966             e.stopEvent();
34967         }
34968         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34969     },
34970
34971     // private
34972     activate : function(autoExpand){
34973         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34974             this.focus();
34975             if(autoExpand){
34976                 this.expandMenu();
34977             }
34978         }
34979         return true;
34980     },
34981
34982     // private
34983     shouldDeactivate : function(e){
34984         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34985             if(this.menu && this.menu.isVisible()){
34986                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34987             }
34988             return true;
34989         }
34990         return false;
34991     },
34992
34993     // private
34994     deactivate : function(){
34995         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34996         this.hideMenu();
34997     },
34998
34999     // private
35000     expandMenu : function(autoActivate){
35001         if(!this.disabled && this.menu){
35002             clearTimeout(this.hideTimer);
35003             delete this.hideTimer;
35004             if(!this.menu.isVisible() && !this.showTimer){
35005                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35006             }else if (this.menu.isVisible() && autoActivate){
35007                 this.menu.tryActivate(0, 1);
35008             }
35009         }
35010     },
35011
35012     // private
35013     deferExpand : function(autoActivate){
35014         delete this.showTimer;
35015         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35016         if(autoActivate){
35017             this.menu.tryActivate(0, 1);
35018         }
35019     },
35020
35021     // private
35022     hideMenu : function(){
35023         clearTimeout(this.showTimer);
35024         delete this.showTimer;
35025         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35026             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35027         }
35028     },
35029
35030     // private
35031     deferHide : function(){
35032         delete this.hideTimer;
35033         this.menu.hide();
35034     }
35035 });/*
35036  * Based on:
35037  * Ext JS Library 1.1.1
35038  * Copyright(c) 2006-2007, Ext JS, LLC.
35039  *
35040  * Originally Released Under LGPL - original licence link has changed is not relivant.
35041  *
35042  * Fork - LGPL
35043  * <script type="text/javascript">
35044  */
35045  
35046 /**
35047  * @class Roo.menu.CheckItem
35048  * @extends Roo.menu.Item
35049  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35050  * @constructor
35051  * Creates a new CheckItem
35052  * @param {Object} config Configuration options
35053  */
35054 Roo.menu.CheckItem = function(config){
35055     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35056     this.addEvents({
35057         /**
35058          * @event beforecheckchange
35059          * Fires before the checked value is set, providing an opportunity to cancel if needed
35060          * @param {Roo.menu.CheckItem} this
35061          * @param {Boolean} checked The new checked value that will be set
35062          */
35063         "beforecheckchange" : true,
35064         /**
35065          * @event checkchange
35066          * Fires after the checked value has been set
35067          * @param {Roo.menu.CheckItem} this
35068          * @param {Boolean} checked The checked value that was set
35069          */
35070         "checkchange" : true
35071     });
35072     if(this.checkHandler){
35073         this.on('checkchange', this.checkHandler, this.scope);
35074     }
35075 };
35076 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35077     /**
35078      * @cfg {String} group
35079      * All check items with the same group name will automatically be grouped into a single-select
35080      * radio button group (defaults to '')
35081      */
35082     /**
35083      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35084      */
35085     itemCls : "x-menu-item x-menu-check-item",
35086     /**
35087      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35088      */
35089     groupClass : "x-menu-group-item",
35090
35091     /**
35092      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35093      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35094      * initialized with checked = true will be rendered as checked.
35095      */
35096     checked: false,
35097
35098     // private
35099     ctype: "Roo.menu.CheckItem",
35100
35101     // private
35102     onRender : function(c){
35103         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35104         if(this.group){
35105             this.el.addClass(this.groupClass);
35106         }
35107         Roo.menu.MenuMgr.registerCheckable(this);
35108         if(this.checked){
35109             this.checked = false;
35110             this.setChecked(true, true);
35111         }
35112     },
35113
35114     // private
35115     destroy : function(){
35116         if(this.rendered){
35117             Roo.menu.MenuMgr.unregisterCheckable(this);
35118         }
35119         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35120     },
35121
35122     /**
35123      * Set the checked state of this item
35124      * @param {Boolean} checked The new checked value
35125      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35126      */
35127     setChecked : function(state, suppressEvent){
35128         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35129             if(this.container){
35130                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35131             }
35132             this.checked = state;
35133             if(suppressEvent !== true){
35134                 this.fireEvent("checkchange", this, state);
35135             }
35136         }
35137     },
35138
35139     // private
35140     handleClick : function(e){
35141        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35142            this.setChecked(!this.checked);
35143        }
35144        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35145     }
35146 });/*
35147  * Based on:
35148  * Ext JS Library 1.1.1
35149  * Copyright(c) 2006-2007, Ext JS, LLC.
35150  *
35151  * Originally Released Under LGPL - original licence link has changed is not relivant.
35152  *
35153  * Fork - LGPL
35154  * <script type="text/javascript">
35155  */
35156  
35157 /**
35158  * @class Roo.menu.DateItem
35159  * @extends Roo.menu.Adapter
35160  * A menu item that wraps the {@link Roo.DatPicker} component.
35161  * @constructor
35162  * Creates a new DateItem
35163  * @param {Object} config Configuration options
35164  */
35165 Roo.menu.DateItem = function(config){
35166     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35167     /** The Roo.DatePicker object @type Roo.DatePicker */
35168     this.picker = this.component;
35169     this.addEvents({select: true});
35170     
35171     this.picker.on("render", function(picker){
35172         picker.getEl().swallowEvent("click");
35173         picker.container.addClass("x-menu-date-item");
35174     });
35175
35176     this.picker.on("select", this.onSelect, this);
35177 };
35178
35179 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35180     // private
35181     onSelect : function(picker, date){
35182         this.fireEvent("select", this, date, picker);
35183         Roo.menu.DateItem.superclass.handleClick.call(this);
35184     }
35185 });/*
35186  * Based on:
35187  * Ext JS Library 1.1.1
35188  * Copyright(c) 2006-2007, Ext JS, LLC.
35189  *
35190  * Originally Released Under LGPL - original licence link has changed is not relivant.
35191  *
35192  * Fork - LGPL
35193  * <script type="text/javascript">
35194  */
35195  
35196 /**
35197  * @class Roo.menu.ColorItem
35198  * @extends Roo.menu.Adapter
35199  * A menu item that wraps the {@link Roo.ColorPalette} component.
35200  * @constructor
35201  * Creates a new ColorItem
35202  * @param {Object} config Configuration options
35203  */
35204 Roo.menu.ColorItem = function(config){
35205     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35206     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35207     this.palette = this.component;
35208     this.relayEvents(this.palette, ["select"]);
35209     if(this.selectHandler){
35210         this.on('select', this.selectHandler, this.scope);
35211     }
35212 };
35213 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35214  * Based on:
35215  * Ext JS Library 1.1.1
35216  * Copyright(c) 2006-2007, Ext JS, LLC.
35217  *
35218  * Originally Released Under LGPL - original licence link has changed is not relivant.
35219  *
35220  * Fork - LGPL
35221  * <script type="text/javascript">
35222  */
35223  
35224
35225 /**
35226  * @class Roo.menu.DateMenu
35227  * @extends Roo.menu.Menu
35228  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35229  * @constructor
35230  * Creates a new DateMenu
35231  * @param {Object} config Configuration options
35232  */
35233 Roo.menu.DateMenu = function(config){
35234     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35235     this.plain = true;
35236     var di = new Roo.menu.DateItem(config);
35237     this.add(di);
35238     /**
35239      * The {@link Roo.DatePicker} instance for this DateMenu
35240      * @type DatePicker
35241      */
35242     this.picker = di.picker;
35243     /**
35244      * @event select
35245      * @param {DatePicker} picker
35246      * @param {Date} date
35247      */
35248     this.relayEvents(di, ["select"]);
35249
35250     this.on('beforeshow', function(){
35251         if(this.picker){
35252             this.picker.hideMonthPicker(true);
35253         }
35254     }, this);
35255 };
35256 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35257     cls:'x-date-menu'
35258 });/*
35259  * Based on:
35260  * Ext JS Library 1.1.1
35261  * Copyright(c) 2006-2007, Ext JS, LLC.
35262  *
35263  * Originally Released Under LGPL - original licence link has changed is not relivant.
35264  *
35265  * Fork - LGPL
35266  * <script type="text/javascript">
35267  */
35268  
35269
35270 /**
35271  * @class Roo.menu.ColorMenu
35272  * @extends Roo.menu.Menu
35273  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35274  * @constructor
35275  * Creates a new ColorMenu
35276  * @param {Object} config Configuration options
35277  */
35278 Roo.menu.ColorMenu = function(config){
35279     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35280     this.plain = true;
35281     var ci = new Roo.menu.ColorItem(config);
35282     this.add(ci);
35283     /**
35284      * The {@link Roo.ColorPalette} instance for this ColorMenu
35285      * @type ColorPalette
35286      */
35287     this.palette = ci.palette;
35288     /**
35289      * @event select
35290      * @param {ColorPalette} palette
35291      * @param {String} color
35292      */
35293     this.relayEvents(ci, ["select"]);
35294 };
35295 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35296  * Based on:
35297  * Ext JS Library 1.1.1
35298  * Copyright(c) 2006-2007, Ext JS, LLC.
35299  *
35300  * Originally Released Under LGPL - original licence link has changed is not relivant.
35301  *
35302  * Fork - LGPL
35303  * <script type="text/javascript">
35304  */
35305  
35306 /**
35307  * @class Roo.form.Field
35308  * @extends Roo.BoxComponent
35309  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35310  * @constructor
35311  * Creates a new Field
35312  * @param {Object} config Configuration options
35313  */
35314 Roo.form.Field = function(config){
35315     Roo.form.Field.superclass.constructor.call(this, config);
35316 };
35317
35318 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35319     /**
35320      * @cfg {String} fieldLabel Label to use when rendering a form.
35321      */
35322        /**
35323      * @cfg {String} qtip Mouse over tip
35324      */
35325      
35326     /**
35327      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35328      */
35329     invalidClass : "x-form-invalid",
35330     /**
35331      * @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")
35332      */
35333     invalidText : "The value in this field is invalid",
35334     /**
35335      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35336      */
35337     focusClass : "x-form-focus",
35338     /**
35339      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35340       automatic validation (defaults to "keyup").
35341      */
35342     validationEvent : "keyup",
35343     /**
35344      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35345      */
35346     validateOnBlur : true,
35347     /**
35348      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35349      */
35350     validationDelay : 250,
35351     /**
35352      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35353      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35354      */
35355     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35356     /**
35357      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35358      */
35359     fieldClass : "x-form-field",
35360     /**
35361      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35362      *<pre>
35363 Value         Description
35364 -----------   ----------------------------------------------------------------------
35365 qtip          Display a quick tip when the user hovers over the field
35366 title         Display a default browser title attribute popup
35367 under         Add a block div beneath the field containing the error text
35368 side          Add an error icon to the right of the field with a popup on hover
35369 [element id]  Add the error text directly to the innerHTML of the specified element
35370 </pre>
35371      */
35372     msgTarget : 'qtip',
35373     /**
35374      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35375      */
35376     msgFx : 'normal',
35377
35378     /**
35379      * @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.
35380      */
35381     readOnly : false,
35382
35383     /**
35384      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35385      */
35386     disabled : false,
35387
35388     /**
35389      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35390      */
35391     inputType : undefined,
35392     
35393     /**
35394      * @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).
35395          */
35396         tabIndex : undefined,
35397         
35398     // private
35399     isFormField : true,
35400
35401     // private
35402     hasFocus : false,
35403     /**
35404      * @property {Roo.Element} fieldEl
35405      * Element Containing the rendered Field (with label etc.)
35406      */
35407     /**
35408      * @cfg {Mixed} value A value to initialize this field with.
35409      */
35410     value : undefined,
35411
35412     /**
35413      * @cfg {String} name The field's HTML name attribute.
35414      */
35415     /**
35416      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35417      */
35418
35419         // private ??
35420         initComponent : function(){
35421         Roo.form.Field.superclass.initComponent.call(this);
35422         this.addEvents({
35423             /**
35424              * @event focus
35425              * Fires when this field receives input focus.
35426              * @param {Roo.form.Field} this
35427              */
35428             focus : true,
35429             /**
35430              * @event blur
35431              * Fires when this field loses input focus.
35432              * @param {Roo.form.Field} this
35433              */
35434             blur : true,
35435             /**
35436              * @event specialkey
35437              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35438              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35439              * @param {Roo.form.Field} this
35440              * @param {Roo.EventObject} e The event object
35441              */
35442             specialkey : true,
35443             /**
35444              * @event change
35445              * Fires just before the field blurs if the field value has changed.
35446              * @param {Roo.form.Field} this
35447              * @param {Mixed} newValue The new value
35448              * @param {Mixed} oldValue The original value
35449              */
35450             change : true,
35451             /**
35452              * @event invalid
35453              * Fires after the field has been marked as invalid.
35454              * @param {Roo.form.Field} this
35455              * @param {String} msg The validation message
35456              */
35457             invalid : true,
35458             /**
35459              * @event valid
35460              * Fires after the field has been validated with no errors.
35461              * @param {Roo.form.Field} this
35462              */
35463             valid : true,
35464              /**
35465              * @event keyup
35466              * Fires after the key up
35467              * @param {Roo.form.Field} this
35468              * @param {Roo.EventObject}  e The event Object
35469              */
35470             keyup : true
35471         });
35472     },
35473
35474     /**
35475      * Returns the name attribute of the field if available
35476      * @return {String} name The field name
35477      */
35478     getName: function(){
35479          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35480     },
35481
35482     // private
35483     onRender : function(ct, position){
35484         Roo.form.Field.superclass.onRender.call(this, ct, position);
35485         if(!this.el){
35486             var cfg = this.getAutoCreate();
35487             if(!cfg.name){
35488                 cfg.name = this.name || this.id;
35489             }
35490             if(this.inputType){
35491                 cfg.type = this.inputType;
35492             }
35493             this.el = ct.createChild(cfg, position);
35494         }
35495         var type = this.el.dom.type;
35496         if(type){
35497             if(type == 'password'){
35498                 type = 'text';
35499             }
35500             this.el.addClass('x-form-'+type);
35501         }
35502         if(this.readOnly){
35503             this.el.dom.readOnly = true;
35504         }
35505         if(this.tabIndex !== undefined){
35506             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35507         }
35508
35509         this.el.addClass([this.fieldClass, this.cls]);
35510         this.initValue();
35511     },
35512
35513     /**
35514      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35515      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35516      * @return {Roo.form.Field} this
35517      */
35518     applyTo : function(target){
35519         this.allowDomMove = false;
35520         this.el = Roo.get(target);
35521         this.render(this.el.dom.parentNode);
35522         return this;
35523     },
35524
35525     // private
35526     initValue : function(){
35527         if(this.value !== undefined){
35528             this.setValue(this.value);
35529         }else if(this.el.dom.value.length > 0){
35530             this.setValue(this.el.dom.value);
35531         }
35532     },
35533
35534     /**
35535      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35536      */
35537     isDirty : function() {
35538         if(this.disabled) {
35539             return false;
35540         }
35541         return String(this.getValue()) !== String(this.originalValue);
35542     },
35543
35544     // private
35545     afterRender : function(){
35546         Roo.form.Field.superclass.afterRender.call(this);
35547         this.initEvents();
35548     },
35549
35550     // private
35551     fireKey : function(e){
35552         //Roo.log('field ' + e.getKey());
35553         if(e.isNavKeyPress()){
35554             this.fireEvent("specialkey", this, e);
35555         }
35556     },
35557
35558     /**
35559      * Resets the current field value to the originally loaded value and clears any validation messages
35560      */
35561     reset : function(){
35562         this.setValue(this.originalValue);
35563         this.clearInvalid();
35564     },
35565
35566     // private
35567     initEvents : function(){
35568         // safari killled keypress - so keydown is now used..
35569         this.el.on("keydown" , this.fireKey,  this);
35570         this.el.on("focus", this.onFocus,  this);
35571         this.el.on("blur", this.onBlur,  this);
35572         this.el.relayEvent('keyup', this);
35573
35574         // reference to original value for reset
35575         this.originalValue = this.getValue();
35576     },
35577
35578     // private
35579     onFocus : function(){
35580         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35581             this.el.addClass(this.focusClass);
35582         }
35583         if(!this.hasFocus){
35584             this.hasFocus = true;
35585             this.startValue = this.getValue();
35586             this.fireEvent("focus", this);
35587         }
35588     },
35589
35590     beforeBlur : Roo.emptyFn,
35591
35592     // private
35593     onBlur : function(){
35594         this.beforeBlur();
35595         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35596             this.el.removeClass(this.focusClass);
35597         }
35598         this.hasFocus = false;
35599         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35600             this.validate();
35601         }
35602         var v = this.getValue();
35603         if(String(v) !== String(this.startValue)){
35604             this.fireEvent('change', this, v, this.startValue);
35605         }
35606         this.fireEvent("blur", this);
35607     },
35608
35609     /**
35610      * Returns whether or not the field value is currently valid
35611      * @param {Boolean} preventMark True to disable marking the field invalid
35612      * @return {Boolean} True if the value is valid, else false
35613      */
35614     isValid : function(preventMark){
35615         if(this.disabled){
35616             return true;
35617         }
35618         var restore = this.preventMark;
35619         this.preventMark = preventMark === true;
35620         var v = this.validateValue(this.processValue(this.getRawValue()));
35621         this.preventMark = restore;
35622         return v;
35623     },
35624
35625     /**
35626      * Validates the field value
35627      * @return {Boolean} True if the value is valid, else false
35628      */
35629     validate : function(){
35630         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35631             this.clearInvalid();
35632             return true;
35633         }
35634         return false;
35635     },
35636
35637     processValue : function(value){
35638         return value;
35639     },
35640
35641     // private
35642     // Subclasses should provide the validation implementation by overriding this
35643     validateValue : function(value){
35644         return true;
35645     },
35646
35647     /**
35648      * Mark this field as invalid
35649      * @param {String} msg The validation message
35650      */
35651     markInvalid : function(msg){
35652         if(!this.rendered || this.preventMark){ // not rendered
35653             return;
35654         }
35655         this.el.addClass(this.invalidClass);
35656         msg = msg || this.invalidText;
35657         switch(this.msgTarget){
35658             case 'qtip':
35659                 this.el.dom.qtip = msg;
35660                 this.el.dom.qclass = 'x-form-invalid-tip';
35661                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35662                     Roo.QuickTips.enable();
35663                 }
35664                 break;
35665             case 'title':
35666                 this.el.dom.title = msg;
35667                 break;
35668             case 'under':
35669                 if(!this.errorEl){
35670                     var elp = this.el.findParent('.x-form-element', 5, true);
35671                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35672                     this.errorEl.setWidth(elp.getWidth(true)-20);
35673                 }
35674                 this.errorEl.update(msg);
35675                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35676                 break;
35677             case 'side':
35678                 if(!this.errorIcon){
35679                     var elp = this.el.findParent('.x-form-element', 5, true);
35680                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35681                 }
35682                 this.alignErrorIcon();
35683                 this.errorIcon.dom.qtip = msg;
35684                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35685                 this.errorIcon.show();
35686                 this.on('resize', this.alignErrorIcon, this);
35687                 break;
35688             default:
35689                 var t = Roo.getDom(this.msgTarget);
35690                 t.innerHTML = msg;
35691                 t.style.display = this.msgDisplay;
35692                 break;
35693         }
35694         this.fireEvent('invalid', this, msg);
35695     },
35696
35697     // private
35698     alignErrorIcon : function(){
35699         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35700     },
35701
35702     /**
35703      * Clear any invalid styles/messages for this field
35704      */
35705     clearInvalid : function(){
35706         if(!this.rendered || this.preventMark){ // not rendered
35707             return;
35708         }
35709         this.el.removeClass(this.invalidClass);
35710         switch(this.msgTarget){
35711             case 'qtip':
35712                 this.el.dom.qtip = '';
35713                 break;
35714             case 'title':
35715                 this.el.dom.title = '';
35716                 break;
35717             case 'under':
35718                 if(this.errorEl){
35719                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35720                 }
35721                 break;
35722             case 'side':
35723                 if(this.errorIcon){
35724                     this.errorIcon.dom.qtip = '';
35725                     this.errorIcon.hide();
35726                     this.un('resize', this.alignErrorIcon, this);
35727                 }
35728                 break;
35729             default:
35730                 var t = Roo.getDom(this.msgTarget);
35731                 t.innerHTML = '';
35732                 t.style.display = 'none';
35733                 break;
35734         }
35735         this.fireEvent('valid', this);
35736     },
35737
35738     /**
35739      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35740      * @return {Mixed} value The field value
35741      */
35742     getRawValue : function(){
35743         var v = this.el.getValue();
35744         if(v === this.emptyText){
35745             v = '';
35746         }
35747         return v;
35748     },
35749
35750     /**
35751      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35752      * @return {Mixed} value The field value
35753      */
35754     getValue : function(){
35755         var v = this.el.getValue();
35756         if(v === this.emptyText || v === undefined){
35757             v = '';
35758         }
35759         return v;
35760     },
35761
35762     /**
35763      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35764      * @param {Mixed} value The value to set
35765      */
35766     setRawValue : function(v){
35767         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35768     },
35769
35770     /**
35771      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35772      * @param {Mixed} value The value to set
35773      */
35774     setValue : function(v){
35775         this.value = v;
35776         if(this.rendered){
35777             this.el.dom.value = (v === null || v === undefined ? '' : v);
35778             this.validate();
35779         }
35780     },
35781
35782     adjustSize : function(w, h){
35783         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35784         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35785         return s;
35786     },
35787
35788     adjustWidth : function(tag, w){
35789         tag = tag.toLowerCase();
35790         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35791             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35792                 if(tag == 'input'){
35793                     return w + 2;
35794                 }
35795                 if(tag = 'textarea'){
35796                     return w-2;
35797                 }
35798             }else if(Roo.isOpera){
35799                 if(tag == 'input'){
35800                     return w + 2;
35801                 }
35802                 if(tag = 'textarea'){
35803                     return w-2;
35804                 }
35805             }
35806         }
35807         return w;
35808     }
35809 });
35810
35811
35812 // anything other than normal should be considered experimental
35813 Roo.form.Field.msgFx = {
35814     normal : {
35815         show: function(msgEl, f){
35816             msgEl.setDisplayed('block');
35817         },
35818
35819         hide : function(msgEl, f){
35820             msgEl.setDisplayed(false).update('');
35821         }
35822     },
35823
35824     slide : {
35825         show: function(msgEl, f){
35826             msgEl.slideIn('t', {stopFx:true});
35827         },
35828
35829         hide : function(msgEl, f){
35830             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35831         }
35832     },
35833
35834     slideRight : {
35835         show: function(msgEl, f){
35836             msgEl.fixDisplay();
35837             msgEl.alignTo(f.el, 'tl-tr');
35838             msgEl.slideIn('l', {stopFx:true});
35839         },
35840
35841         hide : function(msgEl, f){
35842             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35843         }
35844     }
35845 };/*
35846  * Based on:
35847  * Ext JS Library 1.1.1
35848  * Copyright(c) 2006-2007, Ext JS, LLC.
35849  *
35850  * Originally Released Under LGPL - original licence link has changed is not relivant.
35851  *
35852  * Fork - LGPL
35853  * <script type="text/javascript">
35854  */
35855  
35856
35857 /**
35858  * @class Roo.form.TextField
35859  * @extends Roo.form.Field
35860  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35861  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35862  * @constructor
35863  * Creates a new TextField
35864  * @param {Object} config Configuration options
35865  */
35866 Roo.form.TextField = function(config){
35867     Roo.form.TextField.superclass.constructor.call(this, config);
35868     this.addEvents({
35869         /**
35870          * @event autosize
35871          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35872          * according to the default logic, but this event provides a hook for the developer to apply additional
35873          * logic at runtime to resize the field if needed.
35874              * @param {Roo.form.Field} this This text field
35875              * @param {Number} width The new field width
35876              */
35877         autosize : true
35878     });
35879 };
35880
35881 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35882     /**
35883      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35884      */
35885     grow : false,
35886     /**
35887      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35888      */
35889     growMin : 30,
35890     /**
35891      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35892      */
35893     growMax : 800,
35894     /**
35895      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35896      */
35897     vtype : null,
35898     /**
35899      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35900      */
35901     maskRe : null,
35902     /**
35903      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35904      */
35905     disableKeyFilter : false,
35906     /**
35907      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35908      */
35909     allowBlank : true,
35910     /**
35911      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35912      */
35913     minLength : 0,
35914     /**
35915      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35916      */
35917     maxLength : Number.MAX_VALUE,
35918     /**
35919      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35920      */
35921     minLengthText : "The minimum length for this field is {0}",
35922     /**
35923      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35924      */
35925     maxLengthText : "The maximum length for this field is {0}",
35926     /**
35927      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35928      */
35929     selectOnFocus : false,
35930     /**
35931      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35932      */
35933     blankText : "This field is required",
35934     /**
35935      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35936      * If available, this function will be called only after the basic validators all return true, and will be passed the
35937      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35938      */
35939     validator : null,
35940     /**
35941      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35942      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35943      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35944      */
35945     regex : null,
35946     /**
35947      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35948      */
35949     regexText : "",
35950     /**
35951      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35952      */
35953     emptyText : null,
35954     /**
35955      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35956      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35957      */
35958     emptyClass : 'x-form-empty-field',
35959
35960     // private
35961     initEvents : function(){
35962         Roo.form.TextField.superclass.initEvents.call(this);
35963         if(this.validationEvent == 'keyup'){
35964             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35965             this.el.on('keyup', this.filterValidation, this);
35966         }
35967         else if(this.validationEvent !== false){
35968             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35969         }
35970         if(this.selectOnFocus || this.emptyText){
35971             this.on("focus", this.preFocus, this);
35972             if(this.emptyText){
35973                 this.on('blur', this.postBlur, this);
35974                 this.applyEmptyText();
35975             }
35976         }
35977         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35978             this.el.on("keypress", this.filterKeys, this);
35979         }
35980         if(this.grow){
35981             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35982             this.el.on("click", this.autoSize,  this);
35983         }
35984     },
35985
35986     processValue : function(value){
35987         if(this.stripCharsRe){
35988             var newValue = value.replace(this.stripCharsRe, '');
35989             if(newValue !== value){
35990                 this.setRawValue(newValue);
35991                 return newValue;
35992             }
35993         }
35994         return value;
35995     },
35996
35997     filterValidation : function(e){
35998         if(!e.isNavKeyPress()){
35999             this.validationTask.delay(this.validationDelay);
36000         }
36001     },
36002
36003     // private
36004     onKeyUp : function(e){
36005         if(!e.isNavKeyPress()){
36006             this.autoSize();
36007         }
36008     },
36009
36010     /**
36011      * Resets the current field value to the originally-loaded value and clears any validation messages.
36012      * Also adds emptyText and emptyClass if the original value was blank.
36013      */
36014     reset : function(){
36015         Roo.form.TextField.superclass.reset.call(this);
36016         this.applyEmptyText();
36017     },
36018
36019     applyEmptyText : function(){
36020         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36021             this.setRawValue(this.emptyText);
36022             this.el.addClass(this.emptyClass);
36023         }
36024     },
36025
36026     // private
36027     preFocus : function(){
36028         if(this.emptyText){
36029             if(this.el.dom.value == this.emptyText){
36030                 this.setRawValue('');
36031             }
36032             this.el.removeClass(this.emptyClass);
36033         }
36034         if(this.selectOnFocus){
36035             this.el.dom.select();
36036         }
36037     },
36038
36039     // private
36040     postBlur : function(){
36041         this.applyEmptyText();
36042     },
36043
36044     // private
36045     filterKeys : function(e){
36046         var k = e.getKey();
36047         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36048             return;
36049         }
36050         var c = e.getCharCode(), cc = String.fromCharCode(c);
36051         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36052             return;
36053         }
36054         if(!this.maskRe.test(cc)){
36055             e.stopEvent();
36056         }
36057     },
36058
36059     setValue : function(v){
36060         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36061             this.el.removeClass(this.emptyClass);
36062         }
36063         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36064         this.applyEmptyText();
36065         this.autoSize();
36066     },
36067
36068     /**
36069      * Validates a value according to the field's validation rules and marks the field as invalid
36070      * if the validation fails
36071      * @param {Mixed} value The value to validate
36072      * @return {Boolean} True if the value is valid, else false
36073      */
36074     validateValue : function(value){
36075         if(value.length < 1 || value === this.emptyText){ // if it's blank
36076              if(this.allowBlank){
36077                 this.clearInvalid();
36078                 return true;
36079              }else{
36080                 this.markInvalid(this.blankText);
36081                 return false;
36082              }
36083         }
36084         if(value.length < this.minLength){
36085             this.markInvalid(String.format(this.minLengthText, this.minLength));
36086             return false;
36087         }
36088         if(value.length > this.maxLength){
36089             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36090             return false;
36091         }
36092         if(this.vtype){
36093             var vt = Roo.form.VTypes;
36094             if(!vt[this.vtype](value, this)){
36095                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36096                 return false;
36097             }
36098         }
36099         if(typeof this.validator == "function"){
36100             var msg = this.validator(value);
36101             if(msg !== true){
36102                 this.markInvalid(msg);
36103                 return false;
36104             }
36105         }
36106         if(this.regex && !this.regex.test(value)){
36107             this.markInvalid(this.regexText);
36108             return false;
36109         }
36110         return true;
36111     },
36112
36113     /**
36114      * Selects text in this field
36115      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36116      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36117      */
36118     selectText : function(start, end){
36119         var v = this.getRawValue();
36120         if(v.length > 0){
36121             start = start === undefined ? 0 : start;
36122             end = end === undefined ? v.length : end;
36123             var d = this.el.dom;
36124             if(d.setSelectionRange){
36125                 d.setSelectionRange(start, end);
36126             }else if(d.createTextRange){
36127                 var range = d.createTextRange();
36128                 range.moveStart("character", start);
36129                 range.moveEnd("character", v.length-end);
36130                 range.select();
36131             }
36132         }
36133     },
36134
36135     /**
36136      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36137      * This only takes effect if grow = true, and fires the autosize event.
36138      */
36139     autoSize : function(){
36140         if(!this.grow || !this.rendered){
36141             return;
36142         }
36143         if(!this.metrics){
36144             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36145         }
36146         var el = this.el;
36147         var v = el.dom.value;
36148         var d = document.createElement('div');
36149         d.appendChild(document.createTextNode(v));
36150         v = d.innerHTML;
36151         d = null;
36152         v += "&#160;";
36153         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36154         this.el.setWidth(w);
36155         this.fireEvent("autosize", this, w);
36156     }
36157 });/*
36158  * Based on:
36159  * Ext JS Library 1.1.1
36160  * Copyright(c) 2006-2007, Ext JS, LLC.
36161  *
36162  * Originally Released Under LGPL - original licence link has changed is not relivant.
36163  *
36164  * Fork - LGPL
36165  * <script type="text/javascript">
36166  */
36167  
36168 /**
36169  * @class Roo.form.Hidden
36170  * @extends Roo.form.TextField
36171  * Simple Hidden element used on forms 
36172  * 
36173  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36174  * 
36175  * @constructor
36176  * Creates a new Hidden form element.
36177  * @param {Object} config Configuration options
36178  */
36179
36180
36181
36182 // easy hidden field...
36183 Roo.form.Hidden = function(config){
36184     Roo.form.Hidden.superclass.constructor.call(this, config);
36185 };
36186   
36187 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36188     fieldLabel:      '',
36189     inputType:      'hidden',
36190     width:          50,
36191     allowBlank:     true,
36192     labelSeparator: '',
36193     hidden:         true,
36194     itemCls :       'x-form-item-display-none'
36195
36196
36197 });
36198
36199
36200 /*
36201  * Based on:
36202  * Ext JS Library 1.1.1
36203  * Copyright(c) 2006-2007, Ext JS, LLC.
36204  *
36205  * Originally Released Under LGPL - original licence link has changed is not relivant.
36206  *
36207  * Fork - LGPL
36208  * <script type="text/javascript">
36209  */
36210  
36211 /**
36212  * @class Roo.form.TriggerField
36213  * @extends Roo.form.TextField
36214  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36215  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36216  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36217  * for which you can provide a custom implementation.  For example:
36218  * <pre><code>
36219 var trigger = new Roo.form.TriggerField();
36220 trigger.onTriggerClick = myTriggerFn;
36221 trigger.applyTo('my-field');
36222 </code></pre>
36223  *
36224  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36225  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36226  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36227  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36228  * @constructor
36229  * Create a new TriggerField.
36230  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36231  * to the base TextField)
36232  */
36233 Roo.form.TriggerField = function(config){
36234     this.mimicing = false;
36235     Roo.form.TriggerField.superclass.constructor.call(this, config);
36236 };
36237
36238 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36239     /**
36240      * @cfg {String} triggerClass A CSS class to apply to the trigger
36241      */
36242     /**
36243      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36244      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36245      */
36246     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36247     /**
36248      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36249      */
36250     hideTrigger:false,
36251
36252     /** @cfg {Boolean} grow @hide */
36253     /** @cfg {Number} growMin @hide */
36254     /** @cfg {Number} growMax @hide */
36255
36256     /**
36257      * @hide 
36258      * @method
36259      */
36260     autoSize: Roo.emptyFn,
36261     // private
36262     monitorTab : true,
36263     // private
36264     deferHeight : true,
36265
36266     
36267     actionMode : 'wrap',
36268     // private
36269     onResize : function(w, h){
36270         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36271         if(typeof w == 'number'){
36272             var x = w - this.trigger.getWidth();
36273             this.el.setWidth(this.adjustWidth('input', x));
36274             this.trigger.setStyle('left', x+'px');
36275         }
36276     },
36277
36278     // private
36279     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36280
36281     // private
36282     getResizeEl : function(){
36283         return this.wrap;
36284     },
36285
36286     // private
36287     getPositionEl : function(){
36288         return this.wrap;
36289     },
36290
36291     // private
36292     alignErrorIcon : function(){
36293         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36294     },
36295
36296     // private
36297     onRender : function(ct, position){
36298         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36299         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36300         this.trigger = this.wrap.createChild(this.triggerConfig ||
36301                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36302         if(this.hideTrigger){
36303             this.trigger.setDisplayed(false);
36304         }
36305         this.initTrigger();
36306         if(!this.width){
36307             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36308         }
36309     },
36310
36311     // private
36312     initTrigger : function(){
36313         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36314         this.trigger.addClassOnOver('x-form-trigger-over');
36315         this.trigger.addClassOnClick('x-form-trigger-click');
36316     },
36317
36318     // private
36319     onDestroy : function(){
36320         if(this.trigger){
36321             this.trigger.removeAllListeners();
36322             this.trigger.remove();
36323         }
36324         if(this.wrap){
36325             this.wrap.remove();
36326         }
36327         Roo.form.TriggerField.superclass.onDestroy.call(this);
36328     },
36329
36330     // private
36331     onFocus : function(){
36332         Roo.form.TriggerField.superclass.onFocus.call(this);
36333         if(!this.mimicing){
36334             this.wrap.addClass('x-trigger-wrap-focus');
36335             this.mimicing = true;
36336             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36337             if(this.monitorTab){
36338                 this.el.on("keydown", this.checkTab, this);
36339             }
36340         }
36341     },
36342
36343     // private
36344     checkTab : function(e){
36345         if(e.getKey() == e.TAB){
36346             this.triggerBlur();
36347         }
36348     },
36349
36350     // private
36351     onBlur : function(){
36352         // do nothing
36353     },
36354
36355     // private
36356     mimicBlur : function(e, t){
36357         if(!this.wrap.contains(t) && this.validateBlur()){
36358             this.triggerBlur();
36359         }
36360     },
36361
36362     // private
36363     triggerBlur : function(){
36364         this.mimicing = false;
36365         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36366         if(this.monitorTab){
36367             this.el.un("keydown", this.checkTab, this);
36368         }
36369         this.wrap.removeClass('x-trigger-wrap-focus');
36370         Roo.form.TriggerField.superclass.onBlur.call(this);
36371     },
36372
36373     // private
36374     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36375     validateBlur : function(e, t){
36376         return true;
36377     },
36378
36379     // private
36380     onDisable : function(){
36381         Roo.form.TriggerField.superclass.onDisable.call(this);
36382         if(this.wrap){
36383             this.wrap.addClass('x-item-disabled');
36384         }
36385     },
36386
36387     // private
36388     onEnable : function(){
36389         Roo.form.TriggerField.superclass.onEnable.call(this);
36390         if(this.wrap){
36391             this.wrap.removeClass('x-item-disabled');
36392         }
36393     },
36394
36395     // private
36396     onShow : function(){
36397         var ae = this.getActionEl();
36398         
36399         if(ae){
36400             ae.dom.style.display = '';
36401             ae.dom.style.visibility = 'visible';
36402         }
36403     },
36404
36405     // private
36406     
36407     onHide : function(){
36408         var ae = this.getActionEl();
36409         ae.dom.style.display = 'none';
36410     },
36411
36412     /**
36413      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36414      * by an implementing function.
36415      * @method
36416      * @param {EventObject} e
36417      */
36418     onTriggerClick : Roo.emptyFn
36419 });
36420
36421 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36422 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36423 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36424 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36425     initComponent : function(){
36426         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36427
36428         this.triggerConfig = {
36429             tag:'span', cls:'x-form-twin-triggers', cn:[
36430             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36431             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36432         ]};
36433     },
36434
36435     getTrigger : function(index){
36436         return this.triggers[index];
36437     },
36438
36439     initTrigger : function(){
36440         var ts = this.trigger.select('.x-form-trigger', true);
36441         this.wrap.setStyle('overflow', 'hidden');
36442         var triggerField = this;
36443         ts.each(function(t, all, index){
36444             t.hide = function(){
36445                 var w = triggerField.wrap.getWidth();
36446                 this.dom.style.display = 'none';
36447                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36448             };
36449             t.show = function(){
36450                 var w = triggerField.wrap.getWidth();
36451                 this.dom.style.display = '';
36452                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36453             };
36454             var triggerIndex = 'Trigger'+(index+1);
36455
36456             if(this['hide'+triggerIndex]){
36457                 t.dom.style.display = 'none';
36458             }
36459             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36460             t.addClassOnOver('x-form-trigger-over');
36461             t.addClassOnClick('x-form-trigger-click');
36462         }, this);
36463         this.triggers = ts.elements;
36464     },
36465
36466     onTrigger1Click : Roo.emptyFn,
36467     onTrigger2Click : Roo.emptyFn
36468 });/*
36469  * Based on:
36470  * Ext JS Library 1.1.1
36471  * Copyright(c) 2006-2007, Ext JS, LLC.
36472  *
36473  * Originally Released Under LGPL - original licence link has changed is not relivant.
36474  *
36475  * Fork - LGPL
36476  * <script type="text/javascript">
36477  */
36478  
36479 /**
36480  * @class Roo.form.TextArea
36481  * @extends Roo.form.TextField
36482  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36483  * support for auto-sizing.
36484  * @constructor
36485  * Creates a new TextArea
36486  * @param {Object} config Configuration options
36487  */
36488 Roo.form.TextArea = function(config){
36489     Roo.form.TextArea.superclass.constructor.call(this, config);
36490     // these are provided exchanges for backwards compat
36491     // minHeight/maxHeight were replaced by growMin/growMax to be
36492     // compatible with TextField growing config values
36493     if(this.minHeight !== undefined){
36494         this.growMin = this.minHeight;
36495     }
36496     if(this.maxHeight !== undefined){
36497         this.growMax = this.maxHeight;
36498     }
36499 };
36500
36501 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36502     /**
36503      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36504      */
36505     growMin : 60,
36506     /**
36507      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36508      */
36509     growMax: 1000,
36510     /**
36511      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36512      * in the field (equivalent to setting overflow: hidden, defaults to false)
36513      */
36514     preventScrollbars: false,
36515     /**
36516      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36517      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36518      */
36519
36520     // private
36521     onRender : function(ct, position){
36522         if(!this.el){
36523             this.defaultAutoCreate = {
36524                 tag: "textarea",
36525                 style:"width:300px;height:60px;",
36526                 autocomplete: "off"
36527             };
36528         }
36529         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36530         if(this.grow){
36531             this.textSizeEl = Roo.DomHelper.append(document.body, {
36532                 tag: "pre", cls: "x-form-grow-sizer"
36533             });
36534             if(this.preventScrollbars){
36535                 this.el.setStyle("overflow", "hidden");
36536             }
36537             this.el.setHeight(this.growMin);
36538         }
36539     },
36540
36541     onDestroy : function(){
36542         if(this.textSizeEl){
36543             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36544         }
36545         Roo.form.TextArea.superclass.onDestroy.call(this);
36546     },
36547
36548     // private
36549     onKeyUp : function(e){
36550         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36551             this.autoSize();
36552         }
36553     },
36554
36555     /**
36556      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36557      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36558      */
36559     autoSize : function(){
36560         if(!this.grow || !this.textSizeEl){
36561             return;
36562         }
36563         var el = this.el;
36564         var v = el.dom.value;
36565         var ts = this.textSizeEl;
36566
36567         ts.innerHTML = '';
36568         ts.appendChild(document.createTextNode(v));
36569         v = ts.innerHTML;
36570
36571         Roo.fly(ts).setWidth(this.el.getWidth());
36572         if(v.length < 1){
36573             v = "&#160;&#160;";
36574         }else{
36575             if(Roo.isIE){
36576                 v = v.replace(/\n/g, '<p>&#160;</p>');
36577             }
36578             v += "&#160;\n&#160;";
36579         }
36580         ts.innerHTML = v;
36581         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36582         if(h != this.lastHeight){
36583             this.lastHeight = h;
36584             this.el.setHeight(h);
36585             this.fireEvent("autosize", this, h);
36586         }
36587     }
36588 });/*
36589  * Based on:
36590  * Ext JS Library 1.1.1
36591  * Copyright(c) 2006-2007, Ext JS, LLC.
36592  *
36593  * Originally Released Under LGPL - original licence link has changed is not relivant.
36594  *
36595  * Fork - LGPL
36596  * <script type="text/javascript">
36597  */
36598  
36599
36600 /**
36601  * @class Roo.form.NumberField
36602  * @extends Roo.form.TextField
36603  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36604  * @constructor
36605  * Creates a new NumberField
36606  * @param {Object} config Configuration options
36607  */
36608 Roo.form.NumberField = function(config){
36609     Roo.form.NumberField.superclass.constructor.call(this, config);
36610 };
36611
36612 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36613     /**
36614      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36615      */
36616     fieldClass: "x-form-field x-form-num-field",
36617     /**
36618      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36619      */
36620     allowDecimals : true,
36621     /**
36622      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36623      */
36624     decimalSeparator : ".",
36625     /**
36626      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36627      */
36628     decimalPrecision : 2,
36629     /**
36630      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36631      */
36632     allowNegative : true,
36633     /**
36634      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36635      */
36636     minValue : Number.NEGATIVE_INFINITY,
36637     /**
36638      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36639      */
36640     maxValue : Number.MAX_VALUE,
36641     /**
36642      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36643      */
36644     minText : "The minimum value for this field is {0}",
36645     /**
36646      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36647      */
36648     maxText : "The maximum value for this field is {0}",
36649     /**
36650      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36651      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36652      */
36653     nanText : "{0} is not a valid number",
36654
36655     // private
36656     initEvents : function(){
36657         Roo.form.NumberField.superclass.initEvents.call(this);
36658         var allowed = "0123456789";
36659         if(this.allowDecimals){
36660             allowed += this.decimalSeparator;
36661         }
36662         if(this.allowNegative){
36663             allowed += "-";
36664         }
36665         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36666         var keyPress = function(e){
36667             var k = e.getKey();
36668             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36669                 return;
36670             }
36671             var c = e.getCharCode();
36672             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36673                 e.stopEvent();
36674             }
36675         };
36676         this.el.on("keypress", keyPress, this);
36677     },
36678
36679     // private
36680     validateValue : function(value){
36681         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36682             return false;
36683         }
36684         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36685              return true;
36686         }
36687         var num = this.parseValue(value);
36688         if(isNaN(num)){
36689             this.markInvalid(String.format(this.nanText, value));
36690             return false;
36691         }
36692         if(num < this.minValue){
36693             this.markInvalid(String.format(this.minText, this.minValue));
36694             return false;
36695         }
36696         if(num > this.maxValue){
36697             this.markInvalid(String.format(this.maxText, this.maxValue));
36698             return false;
36699         }
36700         return true;
36701     },
36702
36703     getValue : function(){
36704         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36705     },
36706
36707     // private
36708     parseValue : function(value){
36709         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36710         return isNaN(value) ? '' : value;
36711     },
36712
36713     // private
36714     fixPrecision : function(value){
36715         var nan = isNaN(value);
36716         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36717             return nan ? '' : value;
36718         }
36719         return parseFloat(value).toFixed(this.decimalPrecision);
36720     },
36721
36722     setValue : function(v){
36723         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36724     },
36725
36726     // private
36727     decimalPrecisionFcn : function(v){
36728         return Math.floor(v);
36729     },
36730
36731     beforeBlur : function(){
36732         var v = this.parseValue(this.getRawValue());
36733         if(v){
36734             this.setValue(this.fixPrecision(v));
36735         }
36736     }
36737 });/*
36738  * Based on:
36739  * Ext JS Library 1.1.1
36740  * Copyright(c) 2006-2007, Ext JS, LLC.
36741  *
36742  * Originally Released Under LGPL - original licence link has changed is not relivant.
36743  *
36744  * Fork - LGPL
36745  * <script type="text/javascript">
36746  */
36747  
36748 /**
36749  * @class Roo.form.DateField
36750  * @extends Roo.form.TriggerField
36751  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36752 * @constructor
36753 * Create a new DateField
36754 * @param {Object} config
36755  */
36756 Roo.form.DateField = function(config){
36757     Roo.form.DateField.superclass.constructor.call(this, config);
36758     
36759       this.addEvents({
36760          
36761         /**
36762          * @event select
36763          * Fires when a date is selected
36764              * @param {Roo.form.DateField} combo This combo box
36765              * @param {Date} date The date selected
36766              */
36767         'select' : true
36768          
36769     });
36770     
36771     
36772     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36773     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36774     this.ddMatch = null;
36775     if(this.disabledDates){
36776         var dd = this.disabledDates;
36777         var re = "(?:";
36778         for(var i = 0; i < dd.length; i++){
36779             re += dd[i];
36780             if(i != dd.length-1) re += "|";
36781         }
36782         this.ddMatch = new RegExp(re + ")");
36783     }
36784 };
36785
36786 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36787     /**
36788      * @cfg {String} format
36789      * The default date format string which can be overriden for localization support.  The format must be
36790      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36791      */
36792     format : "m/d/y",
36793     /**
36794      * @cfg {String} altFormats
36795      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36796      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36797      */
36798     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36799     /**
36800      * @cfg {Array} disabledDays
36801      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36802      */
36803     disabledDays : null,
36804     /**
36805      * @cfg {String} disabledDaysText
36806      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36807      */
36808     disabledDaysText : "Disabled",
36809     /**
36810      * @cfg {Array} disabledDates
36811      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36812      * expression so they are very powerful. Some examples:
36813      * <ul>
36814      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36815      * <li>["03/08", "09/16"] would disable those days for every year</li>
36816      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36817      * <li>["03/../2006"] would disable every day in March 2006</li>
36818      * <li>["^03"] would disable every day in every March</li>
36819      * </ul>
36820      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36821      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36822      */
36823     disabledDates : null,
36824     /**
36825      * @cfg {String} disabledDatesText
36826      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36827      */
36828     disabledDatesText : "Disabled",
36829     /**
36830      * @cfg {Date/String} minValue
36831      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36832      * valid format (defaults to null).
36833      */
36834     minValue : null,
36835     /**
36836      * @cfg {Date/String} maxValue
36837      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36838      * valid format (defaults to null).
36839      */
36840     maxValue : null,
36841     /**
36842      * @cfg {String} minText
36843      * The error text to display when the date in the cell is before minValue (defaults to
36844      * 'The date in this field must be after {minValue}').
36845      */
36846     minText : "The date in this field must be equal to or after {0}",
36847     /**
36848      * @cfg {String} maxText
36849      * The error text to display when the date in the cell is after maxValue (defaults to
36850      * 'The date in this field must be before {maxValue}').
36851      */
36852     maxText : "The date in this field must be equal to or before {0}",
36853     /**
36854      * @cfg {String} invalidText
36855      * The error text to display when the date in the field is invalid (defaults to
36856      * '{value} is not a valid date - it must be in the format {format}').
36857      */
36858     invalidText : "{0} is not a valid date - it must be in the format {1}",
36859     /**
36860      * @cfg {String} triggerClass
36861      * An additional CSS class used to style the trigger button.  The trigger will always get the
36862      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36863      * which displays a calendar icon).
36864      */
36865     triggerClass : 'x-form-date-trigger',
36866     
36867
36868     /**
36869      * @cfg {bool} useIso
36870      * if enabled, then the date field will use a hidden field to store the 
36871      * real value as iso formated date. default (false)
36872      */ 
36873     useIso : false,
36874     /**
36875      * @cfg {String/Object} autoCreate
36876      * A DomHelper element spec, or true for a default element spec (defaults to
36877      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36878      */ 
36879     // private
36880     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36881     
36882     // private
36883     hiddenField: false,
36884     
36885     onRender : function(ct, position)
36886     {
36887         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36888         if (this.useIso) {
36889             this.el.dom.removeAttribute('name'); 
36890             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36891                     'before', true);
36892             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36893             // prevent input submission
36894             this.hiddenName = this.name;
36895         }
36896             
36897             
36898     },
36899     
36900     // private
36901     validateValue : function(value)
36902     {
36903         value = this.formatDate(value);
36904         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36905             return false;
36906         }
36907         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36908              return true;
36909         }
36910         var svalue = value;
36911         value = this.parseDate(value);
36912         if(!value){
36913             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36914             return false;
36915         }
36916         var time = value.getTime();
36917         if(this.minValue && time < this.minValue.getTime()){
36918             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36919             return false;
36920         }
36921         if(this.maxValue && time > this.maxValue.getTime()){
36922             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36923             return false;
36924         }
36925         if(this.disabledDays){
36926             var day = value.getDay();
36927             for(var i = 0; i < this.disabledDays.length; i++) {
36928                 if(day === this.disabledDays[i]){
36929                     this.markInvalid(this.disabledDaysText);
36930                     return false;
36931                 }
36932             }
36933         }
36934         var fvalue = this.formatDate(value);
36935         if(this.ddMatch && this.ddMatch.test(fvalue)){
36936             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36937             return false;
36938         }
36939         return true;
36940     },
36941
36942     // private
36943     // Provides logic to override the default TriggerField.validateBlur which just returns true
36944     validateBlur : function(){
36945         return !this.menu || !this.menu.isVisible();
36946     },
36947
36948     /**
36949      * Returns the current date value of the date field.
36950      * @return {Date} The date value
36951      */
36952     getValue : function(){
36953         
36954         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36955     },
36956
36957     /**
36958      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36959      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36960      * (the default format used is "m/d/y").
36961      * <br />Usage:
36962      * <pre><code>
36963 //All of these calls set the same date value (May 4, 2006)
36964
36965 //Pass a date object:
36966 var dt = new Date('5/4/06');
36967 dateField.setValue(dt);
36968
36969 //Pass a date string (default format):
36970 dateField.setValue('5/4/06');
36971
36972 //Pass a date string (custom format):
36973 dateField.format = 'Y-m-d';
36974 dateField.setValue('2006-5-4');
36975 </code></pre>
36976      * @param {String/Date} date The date or valid date string
36977      */
36978     setValue : function(date){
36979         if (this.hiddenField) {
36980             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36981         }
36982         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36983     },
36984
36985     // private
36986     parseDate : function(value){
36987         if(!value || value instanceof Date){
36988             return value;
36989         }
36990         var v = Date.parseDate(value, this.format);
36991         if(!v && this.altFormats){
36992             if(!this.altFormatsArray){
36993                 this.altFormatsArray = this.altFormats.split("|");
36994             }
36995             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36996                 v = Date.parseDate(value, this.altFormatsArray[i]);
36997             }
36998         }
36999         return v;
37000     },
37001
37002     // private
37003     formatDate : function(date, fmt){
37004         return (!date || !(date instanceof Date)) ?
37005                date : date.dateFormat(fmt || this.format);
37006     },
37007
37008     // private
37009     menuListeners : {
37010         select: function(m, d){
37011             this.setValue(d);
37012             this.fireEvent('select', this, d);
37013         },
37014         show : function(){ // retain focus styling
37015             this.onFocus();
37016         },
37017         hide : function(){
37018             this.focus.defer(10, this);
37019             var ml = this.menuListeners;
37020             this.menu.un("select", ml.select,  this);
37021             this.menu.un("show", ml.show,  this);
37022             this.menu.un("hide", ml.hide,  this);
37023         }
37024     },
37025
37026     // private
37027     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37028     onTriggerClick : function(){
37029         if(this.disabled){
37030             return;
37031         }
37032         if(this.menu == null){
37033             this.menu = new Roo.menu.DateMenu();
37034         }
37035         Roo.apply(this.menu.picker,  {
37036             showClear: this.allowBlank,
37037             minDate : this.minValue,
37038             maxDate : this.maxValue,
37039             disabledDatesRE : this.ddMatch,
37040             disabledDatesText : this.disabledDatesText,
37041             disabledDays : this.disabledDays,
37042             disabledDaysText : this.disabledDaysText,
37043             format : this.format,
37044             minText : String.format(this.minText, this.formatDate(this.minValue)),
37045             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37046         });
37047         this.menu.on(Roo.apply({}, this.menuListeners, {
37048             scope:this
37049         }));
37050         this.menu.picker.setValue(this.getValue() || new Date());
37051         this.menu.show(this.el, "tl-bl?");
37052     },
37053
37054     beforeBlur : function(){
37055         var v = this.parseDate(this.getRawValue());
37056         if(v){
37057             this.setValue(v);
37058         }
37059     }
37060
37061     /** @cfg {Boolean} grow @hide */
37062     /** @cfg {Number} growMin @hide */
37063     /** @cfg {Number} growMax @hide */
37064     /**
37065      * @hide
37066      * @method autoSize
37067      */
37068 });/*
37069  * Based on:
37070  * Ext JS Library 1.1.1
37071  * Copyright(c) 2006-2007, Ext JS, LLC.
37072  *
37073  * Originally Released Under LGPL - original licence link has changed is not relivant.
37074  *
37075  * Fork - LGPL
37076  * <script type="text/javascript">
37077  */
37078  
37079
37080 /**
37081  * @class Roo.form.ComboBox
37082  * @extends Roo.form.TriggerField
37083  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37084  * @constructor
37085  * Create a new ComboBox.
37086  * @param {Object} config Configuration options
37087  */
37088 Roo.form.ComboBox = function(config){
37089     Roo.form.ComboBox.superclass.constructor.call(this, config);
37090     this.addEvents({
37091         /**
37092          * @event expand
37093          * Fires when the dropdown list is expanded
37094              * @param {Roo.form.ComboBox} combo This combo box
37095              */
37096         'expand' : true,
37097         /**
37098          * @event collapse
37099          * Fires when the dropdown list is collapsed
37100              * @param {Roo.form.ComboBox} combo This combo box
37101              */
37102         'collapse' : true,
37103         /**
37104          * @event beforeselect
37105          * Fires before a list item is selected. Return false to cancel the selection.
37106              * @param {Roo.form.ComboBox} combo This combo box
37107              * @param {Roo.data.Record} record The data record returned from the underlying store
37108              * @param {Number} index The index of the selected item in the dropdown list
37109              */
37110         'beforeselect' : true,
37111         /**
37112          * @event select
37113          * Fires when a list item is selected
37114              * @param {Roo.form.ComboBox} combo This combo box
37115              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37116              * @param {Number} index The index of the selected item in the dropdown list
37117              */
37118         'select' : true,
37119         /**
37120          * @event beforequery
37121          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37122          * The event object passed has these properties:
37123              * @param {Roo.form.ComboBox} combo This combo box
37124              * @param {String} query The query
37125              * @param {Boolean} forceAll true to force "all" query
37126              * @param {Boolean} cancel true to cancel the query
37127              * @param {Object} e The query event object
37128              */
37129         'beforequery': true,
37130          /**
37131          * @event add
37132          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37133              * @param {Roo.form.ComboBox} combo This combo box
37134              */
37135         'add' : true,
37136         /**
37137          * @event edit
37138          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37139              * @param {Roo.form.ComboBox} combo This combo box
37140              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37141              */
37142         'edit' : true
37143         
37144         
37145     });
37146     if(this.transform){
37147         this.allowDomMove = false;
37148         var s = Roo.getDom(this.transform);
37149         if(!this.hiddenName){
37150             this.hiddenName = s.name;
37151         }
37152         if(!this.store){
37153             this.mode = 'local';
37154             var d = [], opts = s.options;
37155             for(var i = 0, len = opts.length;i < len; i++){
37156                 var o = opts[i];
37157                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37158                 if(o.selected) {
37159                     this.value = value;
37160                 }
37161                 d.push([value, o.text]);
37162             }
37163             this.store = new Roo.data.SimpleStore({
37164                 'id': 0,
37165                 fields: ['value', 'text'],
37166                 data : d
37167             });
37168             this.valueField = 'value';
37169             this.displayField = 'text';
37170         }
37171         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37172         if(!this.lazyRender){
37173             this.target = true;
37174             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37175             s.parentNode.removeChild(s); // remove it
37176             this.render(this.el.parentNode);
37177         }else{
37178             s.parentNode.removeChild(s); // remove it
37179         }
37180
37181     }
37182     if (this.store) {
37183         this.store = Roo.factory(this.store, Roo.data);
37184     }
37185     
37186     this.selectedIndex = -1;
37187     if(this.mode == 'local'){
37188         if(config.queryDelay === undefined){
37189             this.queryDelay = 10;
37190         }
37191         if(config.minChars === undefined){
37192             this.minChars = 0;
37193         }
37194     }
37195 };
37196
37197 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37198     /**
37199      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37200      */
37201     /**
37202      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37203      * rendering into an Roo.Editor, defaults to false)
37204      */
37205     /**
37206      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37207      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37208      */
37209     /**
37210      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37211      */
37212     /**
37213      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37214      * the dropdown list (defaults to undefined, with no header element)
37215      */
37216
37217      /**
37218      * @cfg {String/Roo.Template} tpl The template to use to render the output
37219      */
37220      
37221     // private
37222     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37223     /**
37224      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37225      */
37226     listWidth: undefined,
37227     /**
37228      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37229      * mode = 'remote' or 'text' if mode = 'local')
37230      */
37231     displayField: undefined,
37232     /**
37233      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37234      * mode = 'remote' or 'value' if mode = 'local'). 
37235      * Note: use of a valueField requires the user make a selection
37236      * in order for a value to be mapped.
37237      */
37238     valueField: undefined,
37239     /**
37240      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37241      * field's data value (defaults to the underlying DOM element's name)
37242      */
37243     hiddenName: undefined,
37244     /**
37245      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37246      */
37247     listClass: '',
37248     /**
37249      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37250      */
37251     selectedClass: 'x-combo-selected',
37252     /**
37253      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37254      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37255      * which displays a downward arrow icon).
37256      */
37257     triggerClass : 'x-form-arrow-trigger',
37258     /**
37259      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37260      */
37261     shadow:'sides',
37262     /**
37263      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37264      * anchor positions (defaults to 'tl-bl')
37265      */
37266     listAlign: 'tl-bl?',
37267     /**
37268      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37269      */
37270     maxHeight: 300,
37271     /**
37272      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37273      * query specified by the allQuery config option (defaults to 'query')
37274      */
37275     triggerAction: 'query',
37276     /**
37277      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37278      * (defaults to 4, does not apply if editable = false)
37279      */
37280     minChars : 4,
37281     /**
37282      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37283      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37284      */
37285     typeAhead: false,
37286     /**
37287      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37288      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37289      */
37290     queryDelay: 500,
37291     /**
37292      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37293      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37294      */
37295     pageSize: 0,
37296     /**
37297      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37298      * when editable = true (defaults to false)
37299      */
37300     selectOnFocus:false,
37301     /**
37302      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37303      */
37304     queryParam: 'query',
37305     /**
37306      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37307      * when mode = 'remote' (defaults to 'Loading...')
37308      */
37309     loadingText: 'Loading...',
37310     /**
37311      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37312      */
37313     resizable: false,
37314     /**
37315      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37316      */
37317     handleHeight : 8,
37318     /**
37319      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37320      * traditional select (defaults to true)
37321      */
37322     editable: true,
37323     /**
37324      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37325      */
37326     allQuery: '',
37327     /**
37328      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37329      */
37330     mode: 'remote',
37331     /**
37332      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37333      * listWidth has a higher value)
37334      */
37335     minListWidth : 70,
37336     /**
37337      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37338      * allow the user to set arbitrary text into the field (defaults to false)
37339      */
37340     forceSelection:false,
37341     /**
37342      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37343      * if typeAhead = true (defaults to 250)
37344      */
37345     typeAheadDelay : 250,
37346     /**
37347      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37348      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37349      */
37350     valueNotFoundText : undefined,
37351     /**
37352      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37353      */
37354     blockFocus : false,
37355     
37356     /**
37357      * @cfg {Boolean} disableClear Disable showing of clear button.
37358      */
37359     disableClear : false,
37360     /**
37361      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37362      */
37363     alwaysQuery : false,
37364     
37365     //private
37366     addicon : false,
37367     editicon: false,
37368     
37369     
37370     // private
37371     onRender : function(ct, position){
37372         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37373         if(this.hiddenName){
37374             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37375                     'before', true);
37376             this.hiddenField.value =
37377                 this.hiddenValue !== undefined ? this.hiddenValue :
37378                 this.value !== undefined ? this.value : '';
37379
37380             // prevent input submission
37381             this.el.dom.removeAttribute('name');
37382         }
37383         if(Roo.isGecko){
37384             this.el.dom.setAttribute('autocomplete', 'off');
37385         }
37386
37387         var cls = 'x-combo-list';
37388
37389         this.list = new Roo.Layer({
37390             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37391         });
37392
37393         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37394         this.list.setWidth(lw);
37395         this.list.swallowEvent('mousewheel');
37396         this.assetHeight = 0;
37397
37398         if(this.title){
37399             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37400             this.assetHeight += this.header.getHeight();
37401         }
37402
37403         this.innerList = this.list.createChild({cls:cls+'-inner'});
37404         this.innerList.on('mouseover', this.onViewOver, this);
37405         this.innerList.on('mousemove', this.onViewMove, this);
37406         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37407         
37408         if(this.allowBlank && !this.pageSize && !this.disableClear){
37409             this.footer = this.list.createChild({cls:cls+'-ft'});
37410             this.pageTb = new Roo.Toolbar(this.footer);
37411            
37412         }
37413         if(this.pageSize){
37414             this.footer = this.list.createChild({cls:cls+'-ft'});
37415             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37416                     {pageSize: this.pageSize});
37417             
37418         }
37419         
37420         if (this.pageTb && this.allowBlank && !this.disableClear) {
37421             var _this = this;
37422             this.pageTb.add(new Roo.Toolbar.Fill(), {
37423                 cls: 'x-btn-icon x-btn-clear',
37424                 text: '&#160;',
37425                 handler: function()
37426                 {
37427                     _this.collapse();
37428                     _this.clearValue();
37429                     _this.onSelect(false, -1);
37430                 }
37431             });
37432         }
37433         if (this.footer) {
37434             this.assetHeight += this.footer.getHeight();
37435         }
37436         
37437
37438         if(!this.tpl){
37439             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37440         }
37441
37442         this.view = new Roo.View(this.innerList, this.tpl, {
37443             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37444         });
37445
37446         this.view.on('click', this.onViewClick, this);
37447
37448         this.store.on('beforeload', this.onBeforeLoad, this);
37449         this.store.on('load', this.onLoad, this);
37450         this.store.on('loadexception', this.collapse, this);
37451
37452         if(this.resizable){
37453             this.resizer = new Roo.Resizable(this.list,  {
37454                pinned:true, handles:'se'
37455             });
37456             this.resizer.on('resize', function(r, w, h){
37457                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37458                 this.listWidth = w;
37459                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37460                 this.restrictHeight();
37461             }, this);
37462             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37463         }
37464         if(!this.editable){
37465             this.editable = true;
37466             this.setEditable(false);
37467         }  
37468         
37469         
37470         if (typeof(this.events.add.listeners) != 'undefined') {
37471             
37472             this.addicon = this.wrap.createChild(
37473                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37474        
37475             this.addicon.on('click', function(e) {
37476                 this.fireEvent('add', this);
37477             }, this);
37478         }
37479         if (typeof(this.events.edit.listeners) != 'undefined') {
37480             
37481             this.editicon = this.wrap.createChild(
37482                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37483             if (this.addicon) {
37484                 this.editicon.setStyle('margin-left', '40px');
37485             }
37486             this.editicon.on('click', function(e) {
37487                 
37488                 // we fire even  if inothing is selected..
37489                 this.fireEvent('edit', this, this.lastData );
37490                 
37491             }, this);
37492         }
37493         
37494         
37495         
37496     },
37497
37498     // private
37499     initEvents : function(){
37500         Roo.form.ComboBox.superclass.initEvents.call(this);
37501
37502         this.keyNav = new Roo.KeyNav(this.el, {
37503             "up" : function(e){
37504                 this.inKeyMode = true;
37505                 this.selectPrev();
37506             },
37507
37508             "down" : function(e){
37509                 if(!this.isExpanded()){
37510                     this.onTriggerClick();
37511                 }else{
37512                     this.inKeyMode = true;
37513                     this.selectNext();
37514                 }
37515             },
37516
37517             "enter" : function(e){
37518                 this.onViewClick();
37519                 //return true;
37520             },
37521
37522             "esc" : function(e){
37523                 this.collapse();
37524             },
37525
37526             "tab" : function(e){
37527                 this.onViewClick(false);
37528                 return true;
37529             },
37530
37531             scope : this,
37532
37533             doRelay : function(foo, bar, hname){
37534                 if(hname == 'down' || this.scope.isExpanded()){
37535                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37536                 }
37537                 return true;
37538             },
37539
37540             forceKeyDown: true
37541         });
37542         this.queryDelay = Math.max(this.queryDelay || 10,
37543                 this.mode == 'local' ? 10 : 250);
37544         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37545         if(this.typeAhead){
37546             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37547         }
37548         if(this.editable !== false){
37549             this.el.on("keyup", this.onKeyUp, this);
37550         }
37551         if(this.forceSelection){
37552             this.on('blur', this.doForce, this);
37553         }
37554     },
37555
37556     onDestroy : function(){
37557         if(this.view){
37558             this.view.setStore(null);
37559             this.view.el.removeAllListeners();
37560             this.view.el.remove();
37561             this.view.purgeListeners();
37562         }
37563         if(this.list){
37564             this.list.destroy();
37565         }
37566         if(this.store){
37567             this.store.un('beforeload', this.onBeforeLoad, this);
37568             this.store.un('load', this.onLoad, this);
37569             this.store.un('loadexception', this.collapse, this);
37570         }
37571         Roo.form.ComboBox.superclass.onDestroy.call(this);
37572     },
37573
37574     // private
37575     fireKey : function(e){
37576         if(e.isNavKeyPress() && !this.list.isVisible()){
37577             this.fireEvent("specialkey", this, e);
37578         }
37579     },
37580
37581     // private
37582     onResize: function(w, h){
37583         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37584         
37585         if(typeof w != 'number'){
37586             // we do not handle it!?!?
37587             return;
37588         }
37589         var tw = this.trigger.getWidth();
37590         tw += this.addicon ? this.addicon.getWidth() : 0;
37591         tw += this.editicon ? this.editicon.getWidth() : 0;
37592         var x = w - tw;
37593         this.el.setWidth( this.adjustWidth('input', x));
37594             
37595         this.trigger.setStyle('left', x+'px');
37596         
37597         if(this.list && this.listWidth === undefined){
37598             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37599             this.list.setWidth(lw);
37600             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37601         }
37602         
37603     
37604         
37605     },
37606
37607     /**
37608      * Allow or prevent the user from directly editing the field text.  If false is passed,
37609      * the user will only be able to select from the items defined in the dropdown list.  This method
37610      * is the runtime equivalent of setting the 'editable' config option at config time.
37611      * @param {Boolean} value True to allow the user to directly edit the field text
37612      */
37613     setEditable : function(value){
37614         if(value == this.editable){
37615             return;
37616         }
37617         this.editable = value;
37618         if(!value){
37619             this.el.dom.setAttribute('readOnly', true);
37620             this.el.on('mousedown', this.onTriggerClick,  this);
37621             this.el.addClass('x-combo-noedit');
37622         }else{
37623             this.el.dom.setAttribute('readOnly', false);
37624             this.el.un('mousedown', this.onTriggerClick,  this);
37625             this.el.removeClass('x-combo-noedit');
37626         }
37627     },
37628
37629     // private
37630     onBeforeLoad : function(){
37631         if(!this.hasFocus){
37632             return;
37633         }
37634         this.innerList.update(this.loadingText ?
37635                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37636         this.restrictHeight();
37637         this.selectedIndex = -1;
37638     },
37639
37640     // private
37641     onLoad : function(){
37642         if(!this.hasFocus){
37643             return;
37644         }
37645         if(this.store.getCount() > 0){
37646             this.expand();
37647             this.restrictHeight();
37648             if(this.lastQuery == this.allQuery){
37649                 if(this.editable){
37650                     this.el.dom.select();
37651                 }
37652                 if(!this.selectByValue(this.value, true)){
37653                     this.select(0, true);
37654                 }
37655             }else{
37656                 this.selectNext();
37657                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37658                     this.taTask.delay(this.typeAheadDelay);
37659                 }
37660             }
37661         }else{
37662             this.onEmptyResults();
37663         }
37664         //this.el.focus();
37665     },
37666
37667     // private
37668     onTypeAhead : function(){
37669         if(this.store.getCount() > 0){
37670             var r = this.store.getAt(0);
37671             var newValue = r.data[this.displayField];
37672             var len = newValue.length;
37673             var selStart = this.getRawValue().length;
37674             if(selStart != len){
37675                 this.setRawValue(newValue);
37676                 this.selectText(selStart, newValue.length);
37677             }
37678         }
37679     },
37680
37681     // private
37682     onSelect : function(record, index){
37683         if(this.fireEvent('beforeselect', this, record, index) !== false){
37684             this.setFromData(index > -1 ? record.data : false);
37685             this.collapse();
37686             this.fireEvent('select', this, record, index);
37687         }
37688     },
37689
37690     /**
37691      * Returns the currently selected field value or empty string if no value is set.
37692      * @return {String} value The selected value
37693      */
37694     getValue : function(){
37695         if(this.valueField){
37696             return typeof this.value != 'undefined' ? this.value : '';
37697         }else{
37698             return Roo.form.ComboBox.superclass.getValue.call(this);
37699         }
37700     },
37701
37702     /**
37703      * Clears any text/value currently set in the field
37704      */
37705     clearValue : function(){
37706         if(this.hiddenField){
37707             this.hiddenField.value = '';
37708         }
37709         this.value = '';
37710         this.setRawValue('');
37711         this.lastSelectionText = '';
37712         this.applyEmptyText();
37713     },
37714
37715     /**
37716      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37717      * will be displayed in the field.  If the value does not match the data value of an existing item,
37718      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37719      * Otherwise the field will be blank (although the value will still be set).
37720      * @param {String} value The value to match
37721      */
37722     setValue : function(v){
37723         var text = v;
37724         if(this.valueField){
37725             var r = this.findRecord(this.valueField, v);
37726             if(r){
37727                 text = r.data[this.displayField];
37728             }else if(this.valueNotFoundText !== undefined){
37729                 text = this.valueNotFoundText;
37730             }
37731         }
37732         this.lastSelectionText = text;
37733         if(this.hiddenField){
37734             this.hiddenField.value = v;
37735         }
37736         Roo.form.ComboBox.superclass.setValue.call(this, text);
37737         this.value = v;
37738     },
37739     /**
37740      * @property {Object} the last set data for the element
37741      */
37742     
37743     lastData : false,
37744     /**
37745      * Sets the value of the field based on a object which is related to the record format for the store.
37746      * @param {Object} value the value to set as. or false on reset?
37747      */
37748     setFromData : function(o){
37749         var dv = ''; // display value
37750         var vv = ''; // value value..
37751         this.lastData = o;
37752         if (this.displayField) {
37753             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37754         } else {
37755             // this is an error condition!!!
37756             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37757         }
37758         
37759         if(this.valueField){
37760             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37761         }
37762         if(this.hiddenField){
37763             this.hiddenField.value = vv;
37764             
37765             this.lastSelectionText = dv;
37766             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37767             this.value = vv;
37768             return;
37769         }
37770         // no hidden field.. - we store the value in 'value', but still display
37771         // display field!!!!
37772         this.lastSelectionText = dv;
37773         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37774         this.value = vv;
37775         
37776         
37777     },
37778     // private
37779     reset : function(){
37780         // overridden so that last data is reset..
37781         this.setValue(this.originalValue);
37782         this.clearInvalid();
37783         this.lastData = false;
37784     },
37785     // private
37786     findRecord : function(prop, value){
37787         var record;
37788         if(this.store.getCount() > 0){
37789             this.store.each(function(r){
37790                 if(r.data[prop] == value){
37791                     record = r;
37792                     return false;
37793                 }
37794             });
37795         }
37796         return record;
37797     },
37798
37799     // private
37800     onViewMove : function(e, t){
37801         this.inKeyMode = false;
37802     },
37803
37804     // private
37805     onViewOver : function(e, t){
37806         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37807             return;
37808         }
37809         var item = this.view.findItemFromChild(t);
37810         if(item){
37811             var index = this.view.indexOf(item);
37812             this.select(index, false);
37813         }
37814     },
37815
37816     // private
37817     onViewClick : function(doFocus){
37818         var index = this.view.getSelectedIndexes()[0];
37819         var r = this.store.getAt(index);
37820         if(r){
37821             this.onSelect(r, index);
37822         }
37823         if(doFocus !== false && !this.blockFocus){
37824             this.el.focus();
37825         }
37826     },
37827
37828     // private
37829     restrictHeight : function(){
37830         this.innerList.dom.style.height = '';
37831         var inner = this.innerList.dom;
37832         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37833         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37834         this.list.beginUpdate();
37835         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37836         this.list.alignTo(this.el, this.listAlign);
37837         this.list.endUpdate();
37838     },
37839
37840     // private
37841     onEmptyResults : function(){
37842         this.collapse();
37843     },
37844
37845     /**
37846      * Returns true if the dropdown list is expanded, else false.
37847      */
37848     isExpanded : function(){
37849         return this.list.isVisible();
37850     },
37851
37852     /**
37853      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37854      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37855      * @param {String} value The data value of the item to select
37856      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37857      * selected item if it is not currently in view (defaults to true)
37858      * @return {Boolean} True if the value matched an item in the list, else false
37859      */
37860     selectByValue : function(v, scrollIntoView){
37861         if(v !== undefined && v !== null){
37862             var r = this.findRecord(this.valueField || this.displayField, v);
37863             if(r){
37864                 this.select(this.store.indexOf(r), scrollIntoView);
37865                 return true;
37866             }
37867         }
37868         return false;
37869     },
37870
37871     /**
37872      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37873      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37874      * @param {Number} index The zero-based index of the list item to select
37875      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37876      * selected item if it is not currently in view (defaults to true)
37877      */
37878     select : function(index, scrollIntoView){
37879         this.selectedIndex = index;
37880         this.view.select(index);
37881         if(scrollIntoView !== false){
37882             var el = this.view.getNode(index);
37883             if(el){
37884                 this.innerList.scrollChildIntoView(el, false);
37885             }
37886         }
37887     },
37888
37889     // private
37890     selectNext : function(){
37891         var ct = this.store.getCount();
37892         if(ct > 0){
37893             if(this.selectedIndex == -1){
37894                 this.select(0);
37895             }else if(this.selectedIndex < ct-1){
37896                 this.select(this.selectedIndex+1);
37897             }
37898         }
37899     },
37900
37901     // private
37902     selectPrev : function(){
37903         var ct = this.store.getCount();
37904         if(ct > 0){
37905             if(this.selectedIndex == -1){
37906                 this.select(0);
37907             }else if(this.selectedIndex != 0){
37908                 this.select(this.selectedIndex-1);
37909             }
37910         }
37911     },
37912
37913     // private
37914     onKeyUp : function(e){
37915         if(this.editable !== false && !e.isSpecialKey()){
37916             this.lastKey = e.getKey();
37917             this.dqTask.delay(this.queryDelay);
37918         }
37919     },
37920
37921     // private
37922     validateBlur : function(){
37923         return !this.list || !this.list.isVisible();   
37924     },
37925
37926     // private
37927     initQuery : function(){
37928         this.doQuery(this.getRawValue());
37929     },
37930
37931     // private
37932     doForce : function(){
37933         if(this.el.dom.value.length > 0){
37934             this.el.dom.value =
37935                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37936             this.applyEmptyText();
37937         }
37938     },
37939
37940     /**
37941      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37942      * query allowing the query action to be canceled if needed.
37943      * @param {String} query The SQL query to execute
37944      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37945      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37946      * saved in the current store (defaults to false)
37947      */
37948     doQuery : function(q, forceAll){
37949         if(q === undefined || q === null){
37950             q = '';
37951         }
37952         var qe = {
37953             query: q,
37954             forceAll: forceAll,
37955             combo: this,
37956             cancel:false
37957         };
37958         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37959             return false;
37960         }
37961         q = qe.query;
37962         forceAll = qe.forceAll;
37963         if(forceAll === true || (q.length >= this.minChars)){
37964             if(this.lastQuery != q || this.alwaysQuery){
37965                 this.lastQuery = q;
37966                 if(this.mode == 'local'){
37967                     this.selectedIndex = -1;
37968                     if(forceAll){
37969                         this.store.clearFilter();
37970                     }else{
37971                         this.store.filter(this.displayField, q);
37972                     }
37973                     this.onLoad();
37974                 }else{
37975                     this.store.baseParams[this.queryParam] = q;
37976                     this.store.load({
37977                         params: this.getParams(q)
37978                     });
37979                     this.expand();
37980                 }
37981             }else{
37982                 this.selectedIndex = -1;
37983                 this.onLoad();   
37984             }
37985         }
37986     },
37987
37988     // private
37989     getParams : function(q){
37990         var p = {};
37991         //p[this.queryParam] = q;
37992         if(this.pageSize){
37993             p.start = 0;
37994             p.limit = this.pageSize;
37995         }
37996         return p;
37997     },
37998
37999     /**
38000      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38001      */
38002     collapse : function(){
38003         if(!this.isExpanded()){
38004             return;
38005         }
38006         this.list.hide();
38007         Roo.get(document).un('mousedown', this.collapseIf, this);
38008         Roo.get(document).un('mousewheel', this.collapseIf, this);
38009         if (!this.editable) {
38010             Roo.get(document).un('keydown', this.listKeyPress, this);
38011         }
38012         this.fireEvent('collapse', this);
38013     },
38014
38015     // private
38016     collapseIf : function(e){
38017         if(!e.within(this.wrap) && !e.within(this.list)){
38018             this.collapse();
38019         }
38020     },
38021
38022     /**
38023      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38024      */
38025     expand : function(){
38026         if(this.isExpanded() || !this.hasFocus){
38027             return;
38028         }
38029         this.list.alignTo(this.el, this.listAlign);
38030         this.list.show();
38031         Roo.get(document).on('mousedown', this.collapseIf, this);
38032         Roo.get(document).on('mousewheel', this.collapseIf, this);
38033         if (!this.editable) {
38034             Roo.get(document).on('keydown', this.listKeyPress, this);
38035         }
38036         
38037         this.fireEvent('expand', this);
38038     },
38039
38040     // private
38041     // Implements the default empty TriggerField.onTriggerClick function
38042     onTriggerClick : function(){
38043         if(this.disabled){
38044             return;
38045         }
38046         if(this.isExpanded()){
38047             this.collapse();
38048             if (!this.blockFocus) {
38049                 this.el.focus();
38050             }
38051             
38052         }else {
38053             this.hasFocus = true;
38054             if(this.triggerAction == 'all') {
38055                 this.doQuery(this.allQuery, true);
38056             } else {
38057                 this.doQuery(this.getRawValue());
38058             }
38059             if (!this.blockFocus) {
38060                 this.el.focus();
38061             }
38062         }
38063     },
38064     listKeyPress : function(e)
38065     {
38066         //Roo.log('listkeypress');
38067         // scroll to first matching element based on key pres..
38068         if (e.isSpecialKey()) {
38069             return false;
38070         }
38071         var k = String.fromCharCode(e.getKey()).toUpperCase();
38072         //Roo.log(k);
38073         var match  = false;
38074         var csel = this.view.getSelectedNodes();
38075         var cselitem = false;
38076         if (csel.length) {
38077             var ix = this.view.indexOf(csel[0]);
38078             cselitem  = this.store.getAt(ix);
38079             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38080                 cselitem = false;
38081             }
38082             
38083         }
38084         
38085         this.store.each(function(v) { 
38086             if (cselitem) {
38087                 // start at existing selection.
38088                 if (cselitem.id == v.id) {
38089                     cselitem = false;
38090                 }
38091                 return;
38092             }
38093                 
38094             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38095                 match = this.store.indexOf(v);
38096                 return false;
38097             }
38098         }, this);
38099         
38100         if (match === false) {
38101             return true; // no more action?
38102         }
38103         // scroll to?
38104         this.view.select(match);
38105         var sn = Roo.get(this.view.getSelectedNodes()[0])
38106         sn.scrollIntoView(sn.dom.parentNode, false);
38107     }
38108
38109     /** 
38110     * @cfg {Boolean} grow 
38111     * @hide 
38112     */
38113     /** 
38114     * @cfg {Number} growMin 
38115     * @hide 
38116     */
38117     /** 
38118     * @cfg {Number} growMax 
38119     * @hide 
38120     */
38121     /**
38122      * @hide
38123      * @method autoSize
38124      */
38125 });/*
38126  * Based on:
38127  * Ext JS Library 1.1.1
38128  * Copyright(c) 2006-2007, Ext JS, LLC.
38129  *
38130  * Originally Released Under LGPL - original licence link has changed is not relivant.
38131  *
38132  * Fork - LGPL
38133  * <script type="text/javascript">
38134  */
38135 /**
38136  * @class Roo.form.Checkbox
38137  * @extends Roo.form.Field
38138  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38139  * @constructor
38140  * Creates a new Checkbox
38141  * @param {Object} config Configuration options
38142  */
38143 Roo.form.Checkbox = function(config){
38144     Roo.form.Checkbox.superclass.constructor.call(this, config);
38145     this.addEvents({
38146         /**
38147          * @event check
38148          * Fires when the checkbox is checked or unchecked.
38149              * @param {Roo.form.Checkbox} this This checkbox
38150              * @param {Boolean} checked The new checked value
38151              */
38152         check : true
38153     });
38154 };
38155
38156 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38157     /**
38158      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38159      */
38160     focusClass : undefined,
38161     /**
38162      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38163      */
38164     fieldClass: "x-form-field",
38165     /**
38166      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38167      */
38168     checked: false,
38169     /**
38170      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38171      * {tag: "input", type: "checkbox", autocomplete: "off"})
38172      */
38173     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38174     /**
38175      * @cfg {String} boxLabel The text that appears beside the checkbox
38176      */
38177     boxLabel : "",
38178     /**
38179      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38180      */  
38181     inputValue : '1',
38182     /**
38183      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38184      */
38185      valueOff: '0', // value when not checked..
38186
38187     actionMode : 'viewEl', 
38188     //
38189     // private
38190     itemCls : 'x-menu-check-item x-form-item',
38191     groupClass : 'x-menu-group-item',
38192     inputType : 'hidden',
38193     
38194     
38195     inSetChecked: false, // check that we are not calling self...
38196     
38197     inputElement: false, // real input element?
38198     basedOn: false, // ????
38199     
38200     isFormField: true, // not sure where this is needed!!!!
38201
38202     onResize : function(){
38203         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38204         if(!this.boxLabel){
38205             this.el.alignTo(this.wrap, 'c-c');
38206         }
38207     },
38208
38209     initEvents : function(){
38210         Roo.form.Checkbox.superclass.initEvents.call(this);
38211         this.el.on("click", this.onClick,  this);
38212         this.el.on("change", this.onClick,  this);
38213     },
38214
38215
38216     getResizeEl : function(){
38217         return this.wrap;
38218     },
38219
38220     getPositionEl : function(){
38221         return this.wrap;
38222     },
38223
38224     // private
38225     onRender : function(ct, position){
38226         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38227         /*
38228         if(this.inputValue !== undefined){
38229             this.el.dom.value = this.inputValue;
38230         }
38231         */
38232         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38233         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38234         var viewEl = this.wrap.createChild({ 
38235             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38236         this.viewEl = viewEl;   
38237         this.wrap.on('click', this.onClick,  this); 
38238         
38239         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38240         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38241         
38242         
38243         
38244         if(this.boxLabel){
38245             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38246         //    viewEl.on('click', this.onClick,  this); 
38247         }
38248         //if(this.checked){
38249             this.setChecked(this.checked);
38250         //}else{
38251             //this.checked = this.el.dom;
38252         //}
38253
38254     },
38255
38256     // private
38257     initValue : Roo.emptyFn,
38258
38259     /**
38260      * Returns the checked state of the checkbox.
38261      * @return {Boolean} True if checked, else false
38262      */
38263     getValue : function(){
38264         if(this.el){
38265             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38266         }
38267         return this.valueOff;
38268         
38269     },
38270
38271         // private
38272     onClick : function(){ 
38273         this.setChecked(!this.checked);
38274
38275         //if(this.el.dom.checked != this.checked){
38276         //    this.setValue(this.el.dom.checked);
38277        // }
38278     },
38279
38280     /**
38281      * Sets the checked state of the checkbox.
38282      * On is always based on a string comparison between inputValue and the param.
38283      * @param {Boolean/String} value - the value to set 
38284      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38285      */
38286     setValue : function(v,suppressEvent){
38287         
38288         
38289         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38290         //if(this.el && this.el.dom){
38291         //    this.el.dom.checked = this.checked;
38292         //    this.el.dom.defaultChecked = this.checked;
38293         //}
38294         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38295         //this.fireEvent("check", this, this.checked);
38296     },
38297     // private..
38298     setChecked : function(state,suppressEvent)
38299     {
38300         if (this.inSetChecked) {
38301             this.checked = state;
38302             return;
38303         }
38304         
38305     
38306         if(this.wrap){
38307             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38308         }
38309         this.checked = state;
38310         if(suppressEvent !== true){
38311             this.fireEvent('check', this, state);
38312         }
38313         this.inSetChecked = true;
38314         this.el.dom.value = state ? this.inputValue : this.valueOff;
38315         this.inSetChecked = false;
38316         
38317     },
38318     // handle setting of hidden value by some other method!!?!?
38319     setFromHidden: function()
38320     {
38321         if(!this.el){
38322             return;
38323         }
38324         //console.log("SET FROM HIDDEN");
38325         //alert('setFrom hidden');
38326         this.setValue(this.el.dom.value);
38327     },
38328     
38329     onDestroy : function()
38330     {
38331         if(this.viewEl){
38332             Roo.get(this.viewEl).remove();
38333         }
38334          
38335         Roo.form.Checkbox.superclass.onDestroy.call(this);
38336     }
38337
38338 });/*
38339  * Based on:
38340  * Ext JS Library 1.1.1
38341  * Copyright(c) 2006-2007, Ext JS, LLC.
38342  *
38343  * Originally Released Under LGPL - original licence link has changed is not relivant.
38344  *
38345  * Fork - LGPL
38346  * <script type="text/javascript">
38347  */
38348  
38349 /**
38350  * @class Roo.form.Radio
38351  * @extends Roo.form.Checkbox
38352  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38353  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38354  * @constructor
38355  * Creates a new Radio
38356  * @param {Object} config Configuration options
38357  */
38358 Roo.form.Radio = function(){
38359     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38360 };
38361 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38362     inputType: 'radio',
38363
38364     /**
38365      * If this radio is part of a group, it will return the selected value
38366      * @return {String}
38367      */
38368     getGroupValue : function(){
38369         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38370     }
38371 });//<script type="text/javascript">
38372
38373 /*
38374  * Ext JS Library 1.1.1
38375  * Copyright(c) 2006-2007, Ext JS, LLC.
38376  * licensing@extjs.com
38377  * 
38378  * http://www.extjs.com/license
38379  */
38380  
38381  /*
38382   * 
38383   * Known bugs:
38384   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38385   * - IE ? - no idea how much works there.
38386   * 
38387   * 
38388   * 
38389   */
38390  
38391
38392 /**
38393  * @class Ext.form.HtmlEditor
38394  * @extends Ext.form.Field
38395  * Provides a lightweight HTML Editor component.
38396  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38397  * 
38398  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38399  * supported by this editor.</b><br/><br/>
38400  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38401  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38402  */
38403 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38404       /**
38405      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38406      */
38407     toolbars : false,
38408     /**
38409      * @cfg {String} createLinkText The default text for the create link prompt
38410      */
38411     createLinkText : 'Please enter the URL for the link:',
38412     /**
38413      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38414      */
38415     defaultLinkValue : 'http:/'+'/',
38416    
38417     
38418     // id of frame..
38419     frameId: false,
38420     
38421     // private properties
38422     validationEvent : false,
38423     deferHeight: true,
38424     initialized : false,
38425     activated : false,
38426     sourceEditMode : false,
38427     onFocus : Roo.emptyFn,
38428     iframePad:3,
38429     hideMode:'offsets',
38430     defaultAutoCreate : {
38431         tag: "textarea",
38432         style:"width:500px;height:300px;",
38433         autocomplete: "off"
38434     },
38435
38436     // private
38437     initComponent : function(){
38438         this.addEvents({
38439             /**
38440              * @event initialize
38441              * Fires when the editor is fully initialized (including the iframe)
38442              * @param {HtmlEditor} this
38443              */
38444             initialize: true,
38445             /**
38446              * @event activate
38447              * Fires when the editor is first receives the focus. Any insertion must wait
38448              * until after this event.
38449              * @param {HtmlEditor} this
38450              */
38451             activate: true,
38452              /**
38453              * @event beforesync
38454              * Fires before the textarea is updated with content from the editor iframe. Return false
38455              * to cancel the sync.
38456              * @param {HtmlEditor} this
38457              * @param {String} html
38458              */
38459             beforesync: true,
38460              /**
38461              * @event beforepush
38462              * Fires before the iframe editor is updated with content from the textarea. Return false
38463              * to cancel the push.
38464              * @param {HtmlEditor} this
38465              * @param {String} html
38466              */
38467             beforepush: true,
38468              /**
38469              * @event sync
38470              * Fires when the textarea is updated with content from the editor iframe.
38471              * @param {HtmlEditor} this
38472              * @param {String} html
38473              */
38474             sync: true,
38475              /**
38476              * @event push
38477              * Fires when the iframe editor is updated with content from the textarea.
38478              * @param {HtmlEditor} this
38479              * @param {String} html
38480              */
38481             push: true,
38482              /**
38483              * @event editmodechange
38484              * Fires when the editor switches edit modes
38485              * @param {HtmlEditor} this
38486              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38487              */
38488             editmodechange: true,
38489             /**
38490              * @event editorevent
38491              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38492              * @param {HtmlEditor} this
38493              */
38494             editorevent: true
38495         })
38496     },
38497
38498     /**
38499      * Protected method that will not generally be called directly. It
38500      * is called when the editor creates its toolbar. Override this method if you need to
38501      * add custom toolbar buttons.
38502      * @param {HtmlEditor} editor
38503      */
38504     createToolbar : function(editor){
38505         if (!editor.toolbars || !editor.toolbars.length) {
38506             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38507         }
38508         
38509         for (var i =0 ; i < editor.toolbars.length;i++) {
38510             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38511             editor.toolbars[i].init(editor);
38512         }
38513          
38514         
38515     },
38516
38517     /**
38518      * Protected method that will not generally be called directly. It
38519      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38520      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38521      */
38522     getDocMarkup : function(){
38523         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38524     },
38525
38526     // private
38527     onRender : function(ct, position){
38528         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38529         this.el.dom.style.border = '0 none';
38530         this.el.dom.setAttribute('tabIndex', -1);
38531         this.el.addClass('x-hidden');
38532         if(Roo.isIE){ // fix IE 1px bogus margin
38533             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38534         }
38535         this.wrap = this.el.wrap({
38536             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38537         });
38538
38539         this.frameId = Roo.id();
38540         this.createToolbar(this);
38541         
38542         
38543         
38544         
38545       
38546         
38547         var iframe = this.wrap.createChild({
38548             tag: 'iframe',
38549             id: this.frameId,
38550             name: this.frameId,
38551             frameBorder : 'no',
38552             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38553         });
38554         
38555        // console.log(iframe);
38556         //this.wrap.dom.appendChild(iframe);
38557
38558         this.iframe = iframe.dom;
38559
38560          this.assignDocWin();
38561         
38562         this.doc.designMode = 'on';
38563        
38564         this.doc.open();
38565         this.doc.write(this.getDocMarkup());
38566         this.doc.close();
38567
38568         
38569         var task = { // must defer to wait for browser to be ready
38570             run : function(){
38571                 //console.log("run task?" + this.doc.readyState);
38572                 this.assignDocWin();
38573                 if(this.doc.body || this.doc.readyState == 'complete'){
38574                     try {
38575                         this.doc.designMode="on";
38576                     } catch (e) {
38577                         return;
38578                     }
38579                     Roo.TaskMgr.stop(task);
38580                     this.initEditor.defer(10, this);
38581                 }
38582             },
38583             interval : 10,
38584             duration:10000,
38585             scope: this
38586         };
38587         Roo.TaskMgr.start(task);
38588
38589         if(!this.width){
38590             this.setSize(this.el.getSize());
38591         }
38592     },
38593
38594     // private
38595     onResize : function(w, h){
38596         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38597         if(this.el && this.iframe){
38598             if(typeof w == 'number'){
38599                 var aw = w - this.wrap.getFrameWidth('lr');
38600                 this.el.setWidth(this.adjustWidth('textarea', aw));
38601                 this.iframe.style.width = aw + 'px';
38602             }
38603             if(typeof h == 'number'){
38604                 var tbh = 0;
38605                 for (var i =0; i < this.toolbars.length;i++) {
38606                     // fixme - ask toolbars for heights?
38607                     tbh += this.toolbars[i].tb.el.getHeight();
38608                 }
38609                 
38610                 
38611                 
38612                 
38613                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38614                 this.el.setHeight(this.adjustWidth('textarea', ah));
38615                 this.iframe.style.height = ah + 'px';
38616                 if(this.doc){
38617                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38618                 }
38619             }
38620         }
38621     },
38622
38623     /**
38624      * Toggles the editor between standard and source edit mode.
38625      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38626      */
38627     toggleSourceEdit : function(sourceEditMode){
38628         
38629         this.sourceEditMode = sourceEditMode === true;
38630         
38631         if(this.sourceEditMode){
38632           
38633             this.syncValue();
38634             this.iframe.className = 'x-hidden';
38635             this.el.removeClass('x-hidden');
38636             this.el.dom.removeAttribute('tabIndex');
38637             this.el.focus();
38638         }else{
38639              
38640             this.pushValue();
38641             this.iframe.className = '';
38642             this.el.addClass('x-hidden');
38643             this.el.dom.setAttribute('tabIndex', -1);
38644             this.deferFocus();
38645         }
38646         this.setSize(this.wrap.getSize());
38647         this.fireEvent('editmodechange', this, this.sourceEditMode);
38648     },
38649
38650     // private used internally
38651     createLink : function(){
38652         var url = prompt(this.createLinkText, this.defaultLinkValue);
38653         if(url && url != 'http:/'+'/'){
38654             this.relayCmd('createlink', url);
38655         }
38656     },
38657
38658     // private (for BoxComponent)
38659     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38660
38661     // private (for BoxComponent)
38662     getResizeEl : function(){
38663         return this.wrap;
38664     },
38665
38666     // private (for BoxComponent)
38667     getPositionEl : function(){
38668         return this.wrap;
38669     },
38670
38671     // private
38672     initEvents : function(){
38673         this.originalValue = this.getValue();
38674     },
38675
38676     /**
38677      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38678      * @method
38679      */
38680     markInvalid : Roo.emptyFn,
38681     /**
38682      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38683      * @method
38684      */
38685     clearInvalid : Roo.emptyFn,
38686
38687     setValue : function(v){
38688         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38689         this.pushValue();
38690     },
38691
38692     /**
38693      * Protected method that will not generally be called directly. If you need/want
38694      * custom HTML cleanup, this is the method you should override.
38695      * @param {String} html The HTML to be cleaned
38696      * return {String} The cleaned HTML
38697      */
38698     cleanHtml : function(html){
38699         html = String(html);
38700         if(html.length > 5){
38701             if(Roo.isSafari){ // strip safari nonsense
38702                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38703             }
38704         }
38705         if(html == '&nbsp;'){
38706             html = '';
38707         }
38708         return html;
38709     },
38710
38711     /**
38712      * Protected method that will not generally be called directly. Syncs the contents
38713      * of the editor iframe with the textarea.
38714      */
38715     syncValue : function(){
38716         if(this.initialized){
38717             var bd = (this.doc.body || this.doc.documentElement);
38718             this.cleanUpPaste();
38719             var html = bd.innerHTML;
38720             if(Roo.isSafari){
38721                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38722                 var m = bs.match(/text-align:(.*?);/i);
38723                 if(m && m[1]){
38724                     html = '<div style="'+m[0]+'">' + html + '</div>';
38725                 }
38726             }
38727             html = this.cleanHtml(html);
38728             if(this.fireEvent('beforesync', this, html) !== false){
38729                 this.el.dom.value = html;
38730                 this.fireEvent('sync', this, html);
38731             }
38732         }
38733     },
38734
38735     /**
38736      * Protected method that will not generally be called directly. Pushes the value of the textarea
38737      * into the iframe editor.
38738      */
38739     pushValue : function(){
38740         if(this.initialized){
38741             var v = this.el.dom.value;
38742             if(v.length < 1){
38743                 v = '&#160;';
38744             }
38745             
38746             if(this.fireEvent('beforepush', this, v) !== false){
38747                 var d = (this.doc.body || this.doc.documentElement);
38748                 d.innerHTML = v;
38749                 this.cleanUpPaste();
38750                 this.el.dom.value = d.innerHTML;
38751                 this.fireEvent('push', this, v);
38752             }
38753         }
38754     },
38755
38756     // private
38757     deferFocus : function(){
38758         this.focus.defer(10, this);
38759     },
38760
38761     // doc'ed in Field
38762     focus : function(){
38763         if(this.win && !this.sourceEditMode){
38764             this.win.focus();
38765         }else{
38766             this.el.focus();
38767         }
38768     },
38769     
38770     assignDocWin: function()
38771     {
38772         var iframe = this.iframe;
38773         
38774          if(Roo.isIE){
38775             this.doc = iframe.contentWindow.document;
38776             this.win = iframe.contentWindow;
38777         } else {
38778             if (!Roo.get(this.frameId)) {
38779                 return;
38780             }
38781             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38782             this.win = Roo.get(this.frameId).dom.contentWindow;
38783         }
38784     },
38785     
38786     // private
38787     initEditor : function(){
38788         //console.log("INIT EDITOR");
38789         this.assignDocWin();
38790         
38791         
38792         
38793         this.doc.designMode="on";
38794         this.doc.open();
38795         this.doc.write(this.getDocMarkup());
38796         this.doc.close();
38797         
38798         var dbody = (this.doc.body || this.doc.documentElement);
38799         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38800         // this copies styles from the containing element into thsi one..
38801         // not sure why we need all of this..
38802         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38803         ss['background-attachment'] = 'fixed'; // w3c
38804         dbody.bgProperties = 'fixed'; // ie
38805         Roo.DomHelper.applyStyles(dbody, ss);
38806         Roo.EventManager.on(this.doc, {
38807             'mousedown': this.onEditorEvent,
38808             'dblclick': this.onEditorEvent,
38809             'click': this.onEditorEvent,
38810             'keyup': this.onEditorEvent,
38811             buffer:100,
38812             scope: this
38813         });
38814         if(Roo.isGecko){
38815             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38816         }
38817         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38818             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38819         }
38820         this.initialized = true;
38821
38822         this.fireEvent('initialize', this);
38823         this.pushValue();
38824     },
38825
38826     // private
38827     onDestroy : function(){
38828         
38829         
38830         
38831         if(this.rendered){
38832             
38833             for (var i =0; i < this.toolbars.length;i++) {
38834                 // fixme - ask toolbars for heights?
38835                 this.toolbars[i].onDestroy();
38836             }
38837             
38838             this.wrap.dom.innerHTML = '';
38839             this.wrap.remove();
38840         }
38841     },
38842
38843     // private
38844     onFirstFocus : function(){
38845         
38846         this.assignDocWin();
38847         
38848         
38849         this.activated = true;
38850         for (var i =0; i < this.toolbars.length;i++) {
38851             this.toolbars[i].onFirstFocus();
38852         }
38853        
38854         if(Roo.isGecko){ // prevent silly gecko errors
38855             this.win.focus();
38856             var s = this.win.getSelection();
38857             if(!s.focusNode || s.focusNode.nodeType != 3){
38858                 var r = s.getRangeAt(0);
38859                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38860                 r.collapse(true);
38861                 this.deferFocus();
38862             }
38863             try{
38864                 this.execCmd('useCSS', true);
38865                 this.execCmd('styleWithCSS', false);
38866             }catch(e){}
38867         }
38868         this.fireEvent('activate', this);
38869     },
38870
38871     // private
38872     adjustFont: function(btn){
38873         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38874         //if(Roo.isSafari){ // safari
38875         //    adjust *= 2;
38876        // }
38877         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38878         if(Roo.isSafari){ // safari
38879             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38880             v =  (v < 10) ? 10 : v;
38881             v =  (v > 48) ? 48 : v;
38882             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38883             
38884         }
38885         
38886         
38887         v = Math.max(1, v+adjust);
38888         
38889         this.execCmd('FontSize', v  );
38890     },
38891
38892     onEditorEvent : function(e){
38893         this.fireEvent('editorevent', this, e);
38894       //  this.updateToolbar();
38895         this.syncValue();
38896     },
38897
38898     insertTag : function(tg)
38899     {
38900         // could be a bit smarter... -> wrap the current selected tRoo..
38901         
38902         this.execCmd("formatblock",   tg);
38903         
38904     },
38905     
38906     insertText : function(txt)
38907     {
38908         
38909         
38910         range = this.createRange();
38911         range.deleteContents();
38912                //alert(Sender.getAttribute('label'));
38913                
38914         range.insertNode(this.doc.createTextNode(txt));
38915     } ,
38916     
38917     // private
38918     relayBtnCmd : function(btn){
38919         this.relayCmd(btn.cmd);
38920     },
38921
38922     /**
38923      * Executes a Midas editor command on the editor document and performs necessary focus and
38924      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38925      * @param {String} cmd The Midas command
38926      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38927      */
38928     relayCmd : function(cmd, value){
38929         this.win.focus();
38930         this.execCmd(cmd, value);
38931         this.fireEvent('editorevent', this);
38932         //this.updateToolbar();
38933         this.deferFocus();
38934     },
38935
38936     /**
38937      * Executes a Midas editor command directly on the editor document.
38938      * For visual commands, you should use {@link #relayCmd} instead.
38939      * <b>This should only be called after the editor is initialized.</b>
38940      * @param {String} cmd The Midas command
38941      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38942      */
38943     execCmd : function(cmd, value){
38944         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38945         this.syncValue();
38946     },
38947
38948    
38949     /**
38950      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38951      * to insert tRoo.
38952      * @param {String} text
38953      */
38954     insertAtCursor : function(text){
38955         if(!this.activated){
38956             return;
38957         }
38958         if(Roo.isIE){
38959             this.win.focus();
38960             var r = this.doc.selection.createRange();
38961             if(r){
38962                 r.collapse(true);
38963                 r.pasteHTML(text);
38964                 this.syncValue();
38965                 this.deferFocus();
38966             }
38967         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38968             this.win.focus();
38969             this.execCmd('InsertHTML', text);
38970             this.deferFocus();
38971         }
38972     },
38973  // private
38974     mozKeyPress : function(e){
38975         if(e.ctrlKey){
38976             var c = e.getCharCode(), cmd;
38977           
38978             if(c > 0){
38979                 c = String.fromCharCode(c).toLowerCase();
38980                 switch(c){
38981                     case 'b':
38982                         cmd = 'bold';
38983                     break;
38984                     case 'i':
38985                         cmd = 'italic';
38986                     break;
38987                     case 'u':
38988                         cmd = 'underline';
38989                     case 'v':
38990                         this.cleanUpPaste.defer(100, this);
38991                         return;
38992                     break;
38993                 }
38994                 if(cmd){
38995                     this.win.focus();
38996                     this.execCmd(cmd);
38997                     this.deferFocus();
38998                     e.preventDefault();
38999                 }
39000                 
39001             }
39002         }
39003     },
39004
39005     // private
39006     fixKeys : function(){ // load time branching for fastest keydown performance
39007         if(Roo.isIE){
39008             return function(e){
39009                 var k = e.getKey(), r;
39010                 if(k == e.TAB){
39011                     e.stopEvent();
39012                     r = this.doc.selection.createRange();
39013                     if(r){
39014                         r.collapse(true);
39015                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39016                         this.deferFocus();
39017                     }
39018                     return;
39019                 }
39020                 
39021                 if(k == e.ENTER){
39022                     r = this.doc.selection.createRange();
39023                     if(r){
39024                         var target = r.parentElement();
39025                         if(!target || target.tagName.toLowerCase() != 'li'){
39026                             e.stopEvent();
39027                             r.pasteHTML('<br />');
39028                             r.collapse(false);
39029                             r.select();
39030                         }
39031                     }
39032                 }
39033                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39034                     this.cleanUpPaste.defer(100, this);
39035                     return;
39036                 }
39037                 
39038                 
39039             };
39040         }else if(Roo.isOpera){
39041             return function(e){
39042                 var k = e.getKey();
39043                 if(k == e.TAB){
39044                     e.stopEvent();
39045                     this.win.focus();
39046                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39047                     this.deferFocus();
39048                 }
39049                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39050                     this.cleanUpPaste.defer(100, this);
39051                     return;
39052                 }
39053                 
39054             };
39055         }else if(Roo.isSafari){
39056             return function(e){
39057                 var k = e.getKey();
39058                 
39059                 if(k == e.TAB){
39060                     e.stopEvent();
39061                     this.execCmd('InsertText','\t');
39062                     this.deferFocus();
39063                     return;
39064                 }
39065                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39066                     this.cleanUpPaste.defer(100, this);
39067                     return;
39068                 }
39069                 
39070              };
39071         }
39072     }(),
39073     
39074     getAllAncestors: function()
39075     {
39076         var p = this.getSelectedNode();
39077         var a = [];
39078         if (!p) {
39079             a.push(p); // push blank onto stack..
39080             p = this.getParentElement();
39081         }
39082         
39083         
39084         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39085             a.push(p);
39086             p = p.parentNode;
39087         }
39088         a.push(this.doc.body);
39089         return a;
39090     },
39091     lastSel : false,
39092     lastSelNode : false,
39093     
39094     
39095     getSelection : function() 
39096     {
39097         this.assignDocWin();
39098         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39099     },
39100     
39101     getSelectedNode: function() 
39102     {
39103         // this may only work on Gecko!!!
39104         
39105         // should we cache this!!!!
39106         
39107         
39108         
39109          
39110         var range = this.createRange(this.getSelection());
39111         
39112         if (Roo.isIE) {
39113             var parent = range.parentElement();
39114             while (true) {
39115                 var testRange = range.duplicate();
39116                 testRange.moveToElementText(parent);
39117                 if (testRange.inRange(range)) {
39118                     break;
39119                 }
39120                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39121                     break;
39122                 }
39123                 parent = parent.parentElement;
39124             }
39125             return parent;
39126         }
39127         
39128         
39129         var ar = range.endContainer.childNodes;
39130         if (!ar.length) {
39131             ar = range.commonAncestorContainer.childNodes;
39132             //alert(ar.length);
39133         }
39134         var nodes = [];
39135         var other_nodes = [];
39136         var has_other_nodes = false;
39137         for (var i=0;i<ar.length;i++) {
39138             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39139                 continue;
39140             }
39141             // fullly contained node.
39142             
39143             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39144                 nodes.push(ar[i]);
39145                 continue;
39146             }
39147             
39148             // probably selected..
39149             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39150                 other_nodes.push(ar[i]);
39151                 continue;
39152             }
39153             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39154                 continue;
39155             }
39156             
39157             
39158             has_other_nodes = true;
39159         }
39160         if (!nodes.length && other_nodes.length) {
39161             nodes= other_nodes;
39162         }
39163         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39164             return false;
39165         }
39166         
39167         return nodes[0];
39168     },
39169     createRange: function(sel)
39170     {
39171         // this has strange effects when using with 
39172         // top toolbar - not sure if it's a great idea.
39173         //this.editor.contentWindow.focus();
39174         if (typeof sel != "undefined") {
39175             try {
39176                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39177             } catch(e) {
39178                 return this.doc.createRange();
39179             }
39180         } else {
39181             return this.doc.createRange();
39182         }
39183     },
39184     getParentElement: function()
39185     {
39186         
39187         this.assignDocWin();
39188         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39189         
39190         var range = this.createRange(sel);
39191          
39192         try {
39193             var p = range.commonAncestorContainer;
39194             while (p.nodeType == 3) { // text node
39195                 p = p.parentNode;
39196             }
39197             return p;
39198         } catch (e) {
39199             return null;
39200         }
39201     
39202     },
39203     
39204     
39205     
39206     // BC Hacks - cause I cant work out what i was trying to do..
39207     rangeIntersectsNode : function(range, node)
39208     {
39209         var nodeRange = node.ownerDocument.createRange();
39210         try {
39211             nodeRange.selectNode(node);
39212         }
39213         catch (e) {
39214             nodeRange.selectNodeContents(node);
39215         }
39216
39217         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39218                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39219     },
39220     rangeCompareNode : function(range, node) {
39221         var nodeRange = node.ownerDocument.createRange();
39222         try {
39223             nodeRange.selectNode(node);
39224         } catch (e) {
39225             nodeRange.selectNodeContents(node);
39226         }
39227         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39228         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39229
39230         if (nodeIsBefore && !nodeIsAfter)
39231             return 0;
39232         if (!nodeIsBefore && nodeIsAfter)
39233             return 1;
39234         if (nodeIsBefore && nodeIsAfter)
39235             return 2;
39236
39237         return 3;
39238     },
39239
39240     // private? - in a new class?
39241     cleanUpPaste :  function()
39242     {
39243         // cleans up the whole document..
39244       //  console.log('cleanuppaste');
39245         this.cleanUpChildren(this.doc.body);
39246         
39247         
39248     },
39249     cleanUpChildren : function (n)
39250     {
39251         if (!n.childNodes.length) {
39252             return;
39253         }
39254         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39255            this.cleanUpChild(n.childNodes[i]);
39256         }
39257     },
39258     
39259     
39260         
39261     
39262     cleanUpChild : function (node)
39263     {
39264         //console.log(node);
39265         if (node.nodeName == "#text") {
39266             // clean up silly Windows -- stuff?
39267             return; 
39268         }
39269         if (node.nodeName == "#comment") {
39270             node.parentNode.removeChild(node);
39271             // clean up silly Windows -- stuff?
39272             return; 
39273         }
39274         
39275         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39276             // remove node.
39277             node.parentNode.removeChild(node);
39278             return;
39279             
39280         }
39281         if (!node.attributes || !node.attributes.length) {
39282             this.cleanUpChildren(node);
39283             return;
39284         }
39285         
39286         function cleanAttr(n,v)
39287         {
39288             
39289             if (v.match(/^\./) || v.match(/^\//)) {
39290                 return;
39291             }
39292             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39293                 return;
39294             }
39295             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39296             node.removeAttribute(n);
39297             
39298         }
39299         
39300         function cleanStyle(n,v)
39301         {
39302             if (v.match(/expression/)) { //XSS?? should we even bother..
39303                 node.removeAttribute(n);
39304                 return;
39305             }
39306             
39307             
39308             var parts = v.split(/;/);
39309             Roo.each(parts, function(p) {
39310                 p = p.replace(/\s+/g,'');
39311                 if (!p.length) {
39312                     return;
39313                 }
39314                 var l = p.split(':').shift().replace(/\s+/g,'');
39315                 
39316                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39317                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39318                     node.removeAttribute(n);
39319                     return false;
39320                 }
39321             });
39322             
39323             
39324         }
39325         
39326         
39327         for (var i = node.attributes.length-1; i > -1 ; i--) {
39328             var a = node.attributes[i];
39329             //console.log(a);
39330             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39331                 node.removeAttribute(a.name);
39332                 return;
39333             }
39334             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39335                 cleanAttr(a.name,a.value); // fixme..
39336                 return;
39337             }
39338             if (a.name == 'style') {
39339                 cleanStyle(a.name,a.value);
39340             }
39341             /// clean up MS crap..
39342             if (a.name == 'class') {
39343                 if (a.value.match(/^Mso/)) {
39344                     node.className = '';
39345                 }
39346             }
39347             
39348             // style cleanup!?
39349             // class cleanup?
39350             
39351         }
39352         
39353         
39354         this.cleanUpChildren(node);
39355         
39356         
39357     }
39358     
39359     
39360     // hide stuff that is not compatible
39361     /**
39362      * @event blur
39363      * @hide
39364      */
39365     /**
39366      * @event change
39367      * @hide
39368      */
39369     /**
39370      * @event focus
39371      * @hide
39372      */
39373     /**
39374      * @event specialkey
39375      * @hide
39376      */
39377     /**
39378      * @cfg {String} fieldClass @hide
39379      */
39380     /**
39381      * @cfg {String} focusClass @hide
39382      */
39383     /**
39384      * @cfg {String} autoCreate @hide
39385      */
39386     /**
39387      * @cfg {String} inputType @hide
39388      */
39389     /**
39390      * @cfg {String} invalidClass @hide
39391      */
39392     /**
39393      * @cfg {String} invalidText @hide
39394      */
39395     /**
39396      * @cfg {String} msgFx @hide
39397      */
39398     /**
39399      * @cfg {String} validateOnBlur @hide
39400      */
39401 });
39402
39403 Roo.form.HtmlEditor.white = [
39404         'area', 'br', 'img', 'input', 'hr', 'wbr',
39405         
39406        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39407        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39408        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39409        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39410        'table',   'ul',         'xmp', 
39411        
39412        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39413       'thead',   'tr', 
39414      
39415       'dir', 'menu', 'ol', 'ul', 'dl',
39416        
39417       'embed',  'object'
39418 ];
39419
39420
39421 Roo.form.HtmlEditor.black = [
39422     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39423         'applet', // 
39424         'base',   'basefont', 'bgsound', 'blink',  'body', 
39425         'frame',  'frameset', 'head',    'html',   'ilayer', 
39426         'iframe', 'layer',  'link',     'meta',    'object',   
39427         'script', 'style' ,'title',  'xml' // clean later..
39428 ];
39429 Roo.form.HtmlEditor.clean = [
39430     'script', 'style', 'title', 'xml'
39431 ];
39432
39433 // attributes..
39434
39435 Roo.form.HtmlEditor.ablack = [
39436     'on'
39437 ];
39438     
39439 Roo.form.HtmlEditor.aclean = [ 
39440     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39441 ];
39442
39443 // protocols..
39444 Roo.form.HtmlEditor.pwhite= [
39445         'http',  'https',  'mailto'
39446 ];
39447
39448 Roo.form.HtmlEditor.cwhite= [
39449         'text-align',
39450         'font-size'
39451 ];
39452
39453 // <script type="text/javascript">
39454 /*
39455  * Based on
39456  * Ext JS Library 1.1.1
39457  * Copyright(c) 2006-2007, Ext JS, LLC.
39458  *  
39459  
39460  */
39461
39462 /**
39463  * @class Roo.form.HtmlEditorToolbar1
39464  * Basic Toolbar
39465  * 
39466  * Usage:
39467  *
39468  new Roo.form.HtmlEditor({
39469     ....
39470     toolbars : [
39471         new Roo.form.HtmlEditorToolbar1({
39472             disable : { fonts: 1 , format: 1, ..., ... , ...],
39473             btns : [ .... ]
39474         })
39475     }
39476      
39477  * 
39478  * @cfg {Object} disable List of elements to disable..
39479  * @cfg {Array} btns List of additional buttons.
39480  * 
39481  * 
39482  * NEEDS Extra CSS? 
39483  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39484  */
39485  
39486 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39487 {
39488     
39489     Roo.apply(this, config);
39490     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39491     // dont call parent... till later.
39492 }
39493
39494 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39495     
39496     tb: false,
39497     
39498     rendered: false,
39499     
39500     editor : false,
39501     /**
39502      * @cfg {Object} disable  List of toolbar elements to disable
39503          
39504      */
39505     disable : false,
39506       /**
39507      * @cfg {Array} fontFamilies An array of available font families
39508      */
39509     fontFamilies : [
39510         'Arial',
39511         'Courier New',
39512         'Tahoma',
39513         'Times New Roman',
39514         'Verdana'
39515     ],
39516     
39517     specialChars : [
39518            "&#169;",
39519           "&#174;",     
39520           "&#8482;",    
39521           "&#163;" ,    
39522          // "&#8212;",    
39523           "&#8230;",    
39524           "&#247;" ,    
39525         //  "&#225;" ,     ?? a acute?
39526            "&#8364;"    , //Euro
39527        //   "&#8220;"    ,
39528         //  "&#8221;"    ,
39529         //  "&#8226;"    ,
39530           "&#176;"  //   , // degrees
39531
39532          // "&#233;"     , // e ecute
39533          // "&#250;"     , // u ecute?
39534     ],
39535     inputElements : [ 
39536             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39537             "input:submit", "input:button", "select", "textarea", "label" ],
39538     formats : [
39539         ["p"] ,  
39540         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39541         ["pre"],[ "code"], 
39542         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39543     ],
39544      /**
39545      * @cfg {String} defaultFont default font to use.
39546      */
39547     defaultFont: 'tahoma',
39548    
39549     fontSelect : false,
39550     
39551     
39552     formatCombo : false,
39553     
39554     init : function(editor)
39555     {
39556         this.editor = editor;
39557         
39558         
39559         var fid = editor.frameId;
39560         var etb = this;
39561         function btn(id, toggle, handler){
39562             var xid = fid + '-'+ id ;
39563             return {
39564                 id : xid,
39565                 cmd : id,
39566                 cls : 'x-btn-icon x-edit-'+id,
39567                 enableToggle:toggle !== false,
39568                 scope: editor, // was editor...
39569                 handler:handler||editor.relayBtnCmd,
39570                 clickEvent:'mousedown',
39571                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39572                 tabIndex:-1
39573             };
39574         }
39575         
39576         
39577         
39578         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39579         this.tb = tb;
39580          // stop form submits
39581         tb.el.on('click', function(e){
39582             e.preventDefault(); // what does this do?
39583         });
39584
39585         if(!this.disable.font && !Roo.isSafari){
39586             /* why no safari for fonts
39587             editor.fontSelect = tb.el.createChild({
39588                 tag:'select',
39589                 tabIndex: -1,
39590                 cls:'x-font-select',
39591                 html: editor.createFontOptions()
39592             });
39593             editor.fontSelect.on('change', function(){
39594                 var font = editor.fontSelect.dom.value;
39595                 editor.relayCmd('fontname', font);
39596                 editor.deferFocus();
39597             }, editor);
39598             tb.add(
39599                 editor.fontSelect.dom,
39600                 '-'
39601             );
39602             */
39603         };
39604         if(!this.disable.formats){
39605             this.formatCombo = new Roo.form.ComboBox({
39606                 store: new Roo.data.SimpleStore({
39607                     id : 'tag',
39608                     fields: ['tag'],
39609                     data : this.formats // from states.js
39610                 }),
39611                 blockFocus : true,
39612                 //autoCreate : {tag: "div",  size: "20"},
39613                 displayField:'tag',
39614                 typeAhead: false,
39615                 mode: 'local',
39616                 editable : false,
39617                 triggerAction: 'all',
39618                 emptyText:'Add tag',
39619                 selectOnFocus:true,
39620                 width:135,
39621                 listeners : {
39622                     'select': function(c, r, i) {
39623                         editor.insertTag(r.get('tag'));
39624                         editor.focus();
39625                     }
39626                 }
39627
39628             });
39629             tb.addField(this.formatCombo);
39630             
39631         }
39632         
39633         if(!this.disable.format){
39634             tb.add(
39635                 btn('bold'),
39636                 btn('italic'),
39637                 btn('underline')
39638             );
39639         };
39640         if(!this.disable.fontSize){
39641             tb.add(
39642                 '-',
39643                 
39644                 
39645                 btn('increasefontsize', false, editor.adjustFont),
39646                 btn('decreasefontsize', false, editor.adjustFont)
39647             );
39648         };
39649         
39650         
39651         if(this.disable.colors){
39652             tb.add(
39653                 '-', {
39654                     id:editor.frameId +'-forecolor',
39655                     cls:'x-btn-icon x-edit-forecolor',
39656                     clickEvent:'mousedown',
39657                     tooltip: this.buttonTips['forecolor'] || undefined,
39658                     tabIndex:-1,
39659                     menu : new Roo.menu.ColorMenu({
39660                         allowReselect: true,
39661                         focus: Roo.emptyFn,
39662                         value:'000000',
39663                         plain:true,
39664                         selectHandler: function(cp, color){
39665                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39666                             editor.deferFocus();
39667                         },
39668                         scope: editor,
39669                         clickEvent:'mousedown'
39670                     })
39671                 }, {
39672                     id:editor.frameId +'backcolor',
39673                     cls:'x-btn-icon x-edit-backcolor',
39674                     clickEvent:'mousedown',
39675                     tooltip: this.buttonTips['backcolor'] || undefined,
39676                     tabIndex:-1,
39677                     menu : new Roo.menu.ColorMenu({
39678                         focus: Roo.emptyFn,
39679                         value:'FFFFFF',
39680                         plain:true,
39681                         allowReselect: true,
39682                         selectHandler: function(cp, color){
39683                             if(Roo.isGecko){
39684                                 editor.execCmd('useCSS', false);
39685                                 editor.execCmd('hilitecolor', color);
39686                                 editor.execCmd('useCSS', true);
39687                                 editor.deferFocus();
39688                             }else{
39689                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39690                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39691                                 editor.deferFocus();
39692                             }
39693                         },
39694                         scope:editor,
39695                         clickEvent:'mousedown'
39696                     })
39697                 }
39698             );
39699         };
39700         // now add all the items...
39701         
39702
39703         if(!this.disable.alignments){
39704             tb.add(
39705                 '-',
39706                 btn('justifyleft'),
39707                 btn('justifycenter'),
39708                 btn('justifyright')
39709             );
39710         };
39711
39712         //if(!Roo.isSafari){
39713             if(!this.disable.links){
39714                 tb.add(
39715                     '-',
39716                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39717                 );
39718             };
39719
39720             if(!this.disable.lists){
39721                 tb.add(
39722                     '-',
39723                     btn('insertorderedlist'),
39724                     btn('insertunorderedlist')
39725                 );
39726             }
39727             if(!this.disable.sourceEdit){
39728                 tb.add(
39729                     '-',
39730                     btn('sourceedit', true, function(btn){
39731                         this.toggleSourceEdit(btn.pressed);
39732                     })
39733                 );
39734             }
39735         //}
39736         
39737         var smenu = { };
39738         // special menu.. - needs to be tidied up..
39739         if (!this.disable.special) {
39740             smenu = {
39741                 text: "&#169;",
39742                 cls: 'x-edit-none',
39743                 menu : {
39744                     items : []
39745                    }
39746             };
39747             for (var i =0; i < this.specialChars.length; i++) {
39748                 smenu.menu.items.push({
39749                     
39750                     html: this.specialChars[i],
39751                     handler: function(a,b) {
39752                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39753                         
39754                     },
39755                     tabIndex:-1
39756                 });
39757             }
39758             
39759             
39760             tb.add(smenu);
39761             
39762             
39763         }
39764         if (this.btns) {
39765             for(var i =0; i< this.btns.length;i++) {
39766                 var b = this.btns[i];
39767                 b.cls =  'x-edit-none';
39768                 b.scope = editor;
39769                 tb.add(b);
39770             }
39771         
39772         }
39773         
39774         
39775         
39776         // disable everything...
39777         
39778         this.tb.items.each(function(item){
39779            if(item.id != editor.frameId+ '-sourceedit'){
39780                 item.disable();
39781             }
39782         });
39783         this.rendered = true;
39784         
39785         // the all the btns;
39786         editor.on('editorevent', this.updateToolbar, this);
39787         // other toolbars need to implement this..
39788         //editor.on('editmodechange', this.updateToolbar, this);
39789     },
39790     
39791     
39792     
39793     /**
39794      * Protected method that will not generally be called directly. It triggers
39795      * a toolbar update by reading the markup state of the current selection in the editor.
39796      */
39797     updateToolbar: function(){
39798
39799         if(!this.editor.activated){
39800             this.editor.onFirstFocus();
39801             return;
39802         }
39803
39804         var btns = this.tb.items.map, 
39805             doc = this.editor.doc,
39806             frameId = this.editor.frameId;
39807
39808         if(!this.disable.font && !Roo.isSafari){
39809             /*
39810             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39811             if(name != this.fontSelect.dom.value){
39812                 this.fontSelect.dom.value = name;
39813             }
39814             */
39815         }
39816         if(!this.disable.format){
39817             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39818             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39819             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39820         }
39821         if(!this.disable.alignments){
39822             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39823             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39824             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39825         }
39826         if(!Roo.isSafari && !this.disable.lists){
39827             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39828             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39829         }
39830         
39831         var ans = this.editor.getAllAncestors();
39832         if (this.formatCombo) {
39833             
39834             
39835             var store = this.formatCombo.store;
39836             this.formatCombo.setValue("");
39837             for (var i =0; i < ans.length;i++) {
39838                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39839                     // select it..
39840                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39841                     break;
39842                 }
39843             }
39844         }
39845         
39846         
39847         
39848         // hides menus... - so this cant be on a menu...
39849         Roo.menu.MenuMgr.hideAll();
39850
39851         //this.editorsyncValue();
39852     },
39853    
39854     
39855     createFontOptions : function(){
39856         var buf = [], fs = this.fontFamilies, ff, lc;
39857         for(var i = 0, len = fs.length; i< len; i++){
39858             ff = fs[i];
39859             lc = ff.toLowerCase();
39860             buf.push(
39861                 '<option value="',lc,'" style="font-family:',ff,';"',
39862                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39863                     ff,
39864                 '</option>'
39865             );
39866         }
39867         return buf.join('');
39868     },
39869     
39870     toggleSourceEdit : function(sourceEditMode){
39871         if(sourceEditMode === undefined){
39872             sourceEditMode = !this.sourceEditMode;
39873         }
39874         this.sourceEditMode = sourceEditMode === true;
39875         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39876         // just toggle the button?
39877         if(btn.pressed !== this.editor.sourceEditMode){
39878             btn.toggle(this.editor.sourceEditMode);
39879             return;
39880         }
39881         
39882         if(this.sourceEditMode){
39883             this.tb.items.each(function(item){
39884                 if(item.cmd != 'sourceedit'){
39885                     item.disable();
39886                 }
39887             });
39888           
39889         }else{
39890             if(this.initialized){
39891                 this.tb.items.each(function(item){
39892                     item.enable();
39893                 });
39894             }
39895             
39896         }
39897         // tell the editor that it's been pressed..
39898         this.editor.toggleSourceEdit(sourceEditMode);
39899        
39900     },
39901      /**
39902      * Object collection of toolbar tooltips for the buttons in the editor. The key
39903      * is the command id associated with that button and the value is a valid QuickTips object.
39904      * For example:
39905 <pre><code>
39906 {
39907     bold : {
39908         title: 'Bold (Ctrl+B)',
39909         text: 'Make the selected text bold.',
39910         cls: 'x-html-editor-tip'
39911     },
39912     italic : {
39913         title: 'Italic (Ctrl+I)',
39914         text: 'Make the selected text italic.',
39915         cls: 'x-html-editor-tip'
39916     },
39917     ...
39918 </code></pre>
39919     * @type Object
39920      */
39921     buttonTips : {
39922         bold : {
39923             title: 'Bold (Ctrl+B)',
39924             text: 'Make the selected text bold.',
39925             cls: 'x-html-editor-tip'
39926         },
39927         italic : {
39928             title: 'Italic (Ctrl+I)',
39929             text: 'Make the selected text italic.',
39930             cls: 'x-html-editor-tip'
39931         },
39932         underline : {
39933             title: 'Underline (Ctrl+U)',
39934             text: 'Underline the selected text.',
39935             cls: 'x-html-editor-tip'
39936         },
39937         increasefontsize : {
39938             title: 'Grow Text',
39939             text: 'Increase the font size.',
39940             cls: 'x-html-editor-tip'
39941         },
39942         decreasefontsize : {
39943             title: 'Shrink Text',
39944             text: 'Decrease the font size.',
39945             cls: 'x-html-editor-tip'
39946         },
39947         backcolor : {
39948             title: 'Text Highlight Color',
39949             text: 'Change the background color of the selected text.',
39950             cls: 'x-html-editor-tip'
39951         },
39952         forecolor : {
39953             title: 'Font Color',
39954             text: 'Change the color of the selected text.',
39955             cls: 'x-html-editor-tip'
39956         },
39957         justifyleft : {
39958             title: 'Align Text Left',
39959             text: 'Align text to the left.',
39960             cls: 'x-html-editor-tip'
39961         },
39962         justifycenter : {
39963             title: 'Center Text',
39964             text: 'Center text in the editor.',
39965             cls: 'x-html-editor-tip'
39966         },
39967         justifyright : {
39968             title: 'Align Text Right',
39969             text: 'Align text to the right.',
39970             cls: 'x-html-editor-tip'
39971         },
39972         insertunorderedlist : {
39973             title: 'Bullet List',
39974             text: 'Start a bulleted list.',
39975             cls: 'x-html-editor-tip'
39976         },
39977         insertorderedlist : {
39978             title: 'Numbered List',
39979             text: 'Start a numbered list.',
39980             cls: 'x-html-editor-tip'
39981         },
39982         createlink : {
39983             title: 'Hyperlink',
39984             text: 'Make the selected text a hyperlink.',
39985             cls: 'x-html-editor-tip'
39986         },
39987         sourceedit : {
39988             title: 'Source Edit',
39989             text: 'Switch to source editing mode.',
39990             cls: 'x-html-editor-tip'
39991         }
39992     },
39993     // private
39994     onDestroy : function(){
39995         if(this.rendered){
39996             
39997             this.tb.items.each(function(item){
39998                 if(item.menu){
39999                     item.menu.removeAll();
40000                     if(item.menu.el){
40001                         item.menu.el.destroy();
40002                     }
40003                 }
40004                 item.destroy();
40005             });
40006              
40007         }
40008     },
40009     onFirstFocus: function() {
40010         this.tb.items.each(function(item){
40011            item.enable();
40012         });
40013     }
40014 });
40015
40016
40017
40018
40019 // <script type="text/javascript">
40020 /*
40021  * Based on
40022  * Ext JS Library 1.1.1
40023  * Copyright(c) 2006-2007, Ext JS, LLC.
40024  *  
40025  
40026  */
40027
40028  
40029 /**
40030  * @class Roo.form.HtmlEditor.ToolbarContext
40031  * Context Toolbar
40032  * 
40033  * Usage:
40034  *
40035  new Roo.form.HtmlEditor({
40036     ....
40037     toolbars : [
40038         new Roo.form.HtmlEditor.ToolbarStandard(),
40039         new Roo.form.HtmlEditor.ToolbarContext()
40040         })
40041     }
40042      
40043  * 
40044  * @config : {Object} disable List of elements to disable.. (not done yet.)
40045  * 
40046  * 
40047  */
40048
40049 Roo.form.HtmlEditor.ToolbarContext = function(config)
40050 {
40051     
40052     Roo.apply(this, config);
40053     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40054     // dont call parent... till later.
40055 }
40056 Roo.form.HtmlEditor.ToolbarContext.types = {
40057     'IMG' : {
40058         width : {
40059             title: "Width",
40060             width: 40
40061         },
40062         height:  {
40063             title: "Height",
40064             width: 40
40065         },
40066         align: {
40067             title: "Align",
40068             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40069             width : 80
40070             
40071         },
40072         border: {
40073             title: "Border",
40074             width: 40
40075         },
40076         alt: {
40077             title: "Alt",
40078             width: 120
40079         },
40080         src : {
40081             title: "Src",
40082             width: 220
40083         }
40084         
40085     },
40086     'A' : {
40087         name : {
40088             title: "Name",
40089             width: 50
40090         },
40091         href:  {
40092             title: "Href",
40093             width: 220
40094         } // border?
40095         
40096     },
40097     'TABLE' : {
40098         rows : {
40099             title: "Rows",
40100             width: 20
40101         },
40102         cols : {
40103             title: "Cols",
40104             width: 20
40105         },
40106         width : {
40107             title: "Width",
40108             width: 40
40109         },
40110         height : {
40111             title: "Height",
40112             width: 40
40113         },
40114         border : {
40115             title: "Border",
40116             width: 20
40117         }
40118     },
40119     'TD' : {
40120         width : {
40121             title: "Width",
40122             width: 40
40123         },
40124         height : {
40125             title: "Height",
40126             width: 40
40127         },   
40128         align: {
40129             title: "Align",
40130             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40131             width: 40
40132         },
40133         valign: {
40134             title: "Valign",
40135             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40136             width: 40
40137         },
40138         colspan: {
40139             title: "Colspan",
40140             width: 20
40141             
40142         }
40143     },
40144     'INPUT' : {
40145         name : {
40146             title: "name",
40147             width: 120
40148         },
40149         value : {
40150             title: "Value",
40151             width: 120
40152         },
40153         width : {
40154             title: "Width",
40155             width: 40
40156         }
40157     },
40158     'LABEL' : {
40159         'for' : {
40160             title: "For",
40161             width: 120
40162         }
40163     },
40164     'TEXTAREA' : {
40165           name : {
40166             title: "name",
40167             width: 120
40168         },
40169         rows : {
40170             title: "Rows",
40171             width: 20
40172         },
40173         cols : {
40174             title: "Cols",
40175             width: 20
40176         }
40177     },
40178     'SELECT' : {
40179         name : {
40180             title: "name",
40181             width: 120
40182         },
40183         selectoptions : {
40184             title: "Options",
40185             width: 200
40186         }
40187     },
40188     'BODY' : {
40189         title : {
40190             title: "title",
40191             width: 120,
40192             disabled : true
40193         }
40194     }
40195 };
40196
40197
40198
40199 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40200     
40201     tb: false,
40202     
40203     rendered: false,
40204     
40205     editor : false,
40206     /**
40207      * @cfg {Object} disable  List of toolbar elements to disable
40208          
40209      */
40210     disable : false,
40211     
40212     
40213     
40214     toolbars : false,
40215     
40216     init : function(editor)
40217     {
40218         this.editor = editor;
40219         
40220         
40221         var fid = editor.frameId;
40222         var etb = this;
40223         function btn(id, toggle, handler){
40224             var xid = fid + '-'+ id ;
40225             return {
40226                 id : xid,
40227                 cmd : id,
40228                 cls : 'x-btn-icon x-edit-'+id,
40229                 enableToggle:toggle !== false,
40230                 scope: editor, // was editor...
40231                 handler:handler||editor.relayBtnCmd,
40232                 clickEvent:'mousedown',
40233                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40234                 tabIndex:-1
40235             };
40236         }
40237         // create a new element.
40238         var wdiv = editor.wrap.createChild({
40239                 tag: 'div'
40240             }, editor.wrap.dom.firstChild.nextSibling, true);
40241         
40242         // can we do this more than once??
40243         
40244          // stop form submits
40245       
40246  
40247         // disable everything...
40248         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40249         this.toolbars = {};
40250            
40251         for (var i in  ty) {
40252           
40253             this.toolbars[i] = this.buildToolbar(ty[i],i);
40254         }
40255         this.tb = this.toolbars.BODY;
40256         this.tb.el.show();
40257         
40258          
40259         this.rendered = true;
40260         
40261         // the all the btns;
40262         editor.on('editorevent', this.updateToolbar, this);
40263         // other toolbars need to implement this..
40264         //editor.on('editmodechange', this.updateToolbar, this);
40265     },
40266     
40267     
40268     
40269     /**
40270      * Protected method that will not generally be called directly. It triggers
40271      * a toolbar update by reading the markup state of the current selection in the editor.
40272      */
40273     updateToolbar: function(){
40274
40275         if(!this.editor.activated){
40276             this.editor.onFirstFocus();
40277             return;
40278         }
40279
40280         
40281         var ans = this.editor.getAllAncestors();
40282         
40283         // pick
40284         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40285         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40286         sel = sel ? sel : this.editor.doc.body;
40287         sel = sel.tagName.length ? sel : this.editor.doc.body;
40288         var tn = sel.tagName.toUpperCase();
40289         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40290         tn = sel.tagName.toUpperCase();
40291         if (this.tb.name  == tn) {
40292             return; // no change
40293         }
40294         this.tb.el.hide();
40295         ///console.log("show: " + tn);
40296         this.tb =  this.toolbars[tn];
40297         this.tb.el.show();
40298         this.tb.fields.each(function(e) {
40299             e.setValue(sel.getAttribute(e.name));
40300         });
40301         this.tb.selectedNode = sel;
40302         
40303         
40304         Roo.menu.MenuMgr.hideAll();
40305
40306         //this.editorsyncValue();
40307     },
40308    
40309        
40310     // private
40311     onDestroy : function(){
40312         if(this.rendered){
40313             
40314             this.tb.items.each(function(item){
40315                 if(item.menu){
40316                     item.menu.removeAll();
40317                     if(item.menu.el){
40318                         item.menu.el.destroy();
40319                     }
40320                 }
40321                 item.destroy();
40322             });
40323              
40324         }
40325     },
40326     onFirstFocus: function() {
40327         // need to do this for all the toolbars..
40328         this.tb.items.each(function(item){
40329            item.enable();
40330         });
40331     },
40332     buildToolbar: function(tlist, nm)
40333     {
40334         var editor = this.editor;
40335          // create a new element.
40336         var wdiv = editor.wrap.createChild({
40337                 tag: 'div'
40338             }, editor.wrap.dom.firstChild.nextSibling, true);
40339         
40340        
40341         var tb = new Roo.Toolbar(wdiv);
40342         tb.add(nm+ ":&nbsp;");
40343         for (var i in tlist) {
40344             var item = tlist[i];
40345             tb.add(item.title + ":&nbsp;");
40346             if (item.opts) {
40347                 // fixme
40348                 
40349               
40350                 tb.addField( new Roo.form.ComboBox({
40351                     store: new Roo.data.SimpleStore({
40352                         id : 'val',
40353                         fields: ['val'],
40354                         data : item.opts // from states.js
40355                     }),
40356                     name : i,
40357                     displayField:'val',
40358                     typeAhead: false,
40359                     mode: 'local',
40360                     editable : false,
40361                     triggerAction: 'all',
40362                     emptyText:'Select',
40363                     selectOnFocus:true,
40364                     width: item.width ? item.width  : 130,
40365                     listeners : {
40366                         'select': function(c, r, i) {
40367                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40368                         }
40369                     }
40370
40371                 }));
40372                 continue;
40373                     
40374                 
40375                 
40376                 
40377                 
40378                 tb.addField( new Roo.form.TextField({
40379                     name: i,
40380                     width: 100,
40381                     //allowBlank:false,
40382                     value: ''
40383                 }));
40384                 continue;
40385             }
40386             tb.addField( new Roo.form.TextField({
40387                 name: i,
40388                 width: item.width,
40389                 //allowBlank:true,
40390                 value: '',
40391                 listeners: {
40392                     'change' : function(f, nv, ov) {
40393                         tb.selectedNode.setAttribute(f.name, nv);
40394                     }
40395                 }
40396             }));
40397              
40398         }
40399         tb.el.on('click', function(e){
40400             e.preventDefault(); // what does this do?
40401         });
40402         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40403         tb.el.hide();
40404         tb.name = nm;
40405         // dont need to disable them... as they will get hidden
40406         return tb;
40407          
40408         
40409     }
40410     
40411     
40412     
40413     
40414 });
40415
40416
40417
40418
40419
40420 /*
40421  * Based on:
40422  * Ext JS Library 1.1.1
40423  * Copyright(c) 2006-2007, Ext JS, LLC.
40424  *
40425  * Originally Released Under LGPL - original licence link has changed is not relivant.
40426  *
40427  * Fork - LGPL
40428  * <script type="text/javascript">
40429  */
40430  
40431 /**
40432  * @class Roo.form.BasicForm
40433  * @extends Roo.util.Observable
40434  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40435  * @constructor
40436  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40437  * @param {Object} config Configuration options
40438  */
40439 Roo.form.BasicForm = function(el, config){
40440     this.allItems = [];
40441     this.childForms = [];
40442     Roo.apply(this, config);
40443     /*
40444      * The Roo.form.Field items in this form.
40445      * @type MixedCollection
40446      */
40447      
40448      
40449     this.items = new Roo.util.MixedCollection(false, function(o){
40450         return o.id || (o.id = Roo.id());
40451     });
40452     this.addEvents({
40453         /**
40454          * @event beforeaction
40455          * Fires before any action is performed. Return false to cancel the action.
40456          * @param {Form} this
40457          * @param {Action} action The action to be performed
40458          */
40459         beforeaction: true,
40460         /**
40461          * @event actionfailed
40462          * Fires when an action fails.
40463          * @param {Form} this
40464          * @param {Action} action The action that failed
40465          */
40466         actionfailed : true,
40467         /**
40468          * @event actioncomplete
40469          * Fires when an action is completed.
40470          * @param {Form} this
40471          * @param {Action} action The action that completed
40472          */
40473         actioncomplete : true
40474     });
40475     if(el){
40476         this.initEl(el);
40477     }
40478     Roo.form.BasicForm.superclass.constructor.call(this);
40479 };
40480
40481 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40482     /**
40483      * @cfg {String} method
40484      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40485      */
40486     /**
40487      * @cfg {DataReader} reader
40488      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40489      * This is optional as there is built-in support for processing JSON.
40490      */
40491     /**
40492      * @cfg {DataReader} errorReader
40493      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40494      * This is completely optional as there is built-in support for processing JSON.
40495      */
40496     /**
40497      * @cfg {String} url
40498      * The URL to use for form actions if one isn't supplied in the action options.
40499      */
40500     /**
40501      * @cfg {Boolean} fileUpload
40502      * Set to true if this form is a file upload.
40503      */
40504      
40505     /**
40506      * @cfg {Object} baseParams
40507      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40508      */
40509      /**
40510      
40511     /**
40512      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40513      */
40514     timeout: 30,
40515
40516     // private
40517     activeAction : null,
40518
40519     /**
40520      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40521      * or setValues() data instead of when the form was first created.
40522      */
40523     trackResetOnLoad : false,
40524     
40525     
40526     /**
40527      * childForms - used for multi-tab forms
40528      * @type {Array}
40529      */
40530     childForms : false,
40531     
40532     /**
40533      * allItems - full list of fields.
40534      * @type {Array}
40535      */
40536     allItems : false,
40537     
40538     /**
40539      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40540      * element by passing it or its id or mask the form itself by passing in true.
40541      * @type Mixed
40542      */
40543     waitMsgTarget : false,
40544
40545     // private
40546     initEl : function(el){
40547         this.el = Roo.get(el);
40548         this.id = this.el.id || Roo.id();
40549         this.el.on('submit', this.onSubmit, this);
40550         this.el.addClass('x-form');
40551     },
40552
40553     // private
40554     onSubmit : function(e){
40555         e.stopEvent();
40556     },
40557
40558     /**
40559      * Returns true if client-side validation on the form is successful.
40560      * @return Boolean
40561      */
40562     isValid : function(){
40563         var valid = true;
40564         this.items.each(function(f){
40565            if(!f.validate()){
40566                valid = false;
40567            }
40568         });
40569         return valid;
40570     },
40571
40572     /**
40573      * Returns true if any fields in this form have changed since their original load.
40574      * @return Boolean
40575      */
40576     isDirty : function(){
40577         var dirty = false;
40578         this.items.each(function(f){
40579            if(f.isDirty()){
40580                dirty = true;
40581                return false;
40582            }
40583         });
40584         return dirty;
40585     },
40586
40587     /**
40588      * Performs a predefined action (submit or load) or custom actions you define on this form.
40589      * @param {String} actionName The name of the action type
40590      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40591      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40592      * accept other config options):
40593      * <pre>
40594 Property          Type             Description
40595 ----------------  ---------------  ----------------------------------------------------------------------------------
40596 url               String           The url for the action (defaults to the form's url)
40597 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40598 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40599 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40600                                    validate the form on the client (defaults to false)
40601      * </pre>
40602      * @return {BasicForm} this
40603      */
40604     doAction : function(action, options){
40605         if(typeof action == 'string'){
40606             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40607         }
40608         if(this.fireEvent('beforeaction', this, action) !== false){
40609             this.beforeAction(action);
40610             action.run.defer(100, action);
40611         }
40612         return this;
40613     },
40614
40615     /**
40616      * Shortcut to do a submit action.
40617      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40618      * @return {BasicForm} this
40619      */
40620     submit : function(options){
40621         this.doAction('submit', options);
40622         return this;
40623     },
40624
40625     /**
40626      * Shortcut to do a load action.
40627      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40628      * @return {BasicForm} this
40629      */
40630     load : function(options){
40631         this.doAction('load', options);
40632         return this;
40633     },
40634
40635     /**
40636      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40637      * @param {Record} record The record to edit
40638      * @return {BasicForm} this
40639      */
40640     updateRecord : function(record){
40641         record.beginEdit();
40642         var fs = record.fields;
40643         fs.each(function(f){
40644             var field = this.findField(f.name);
40645             if(field){
40646                 record.set(f.name, field.getValue());
40647             }
40648         }, this);
40649         record.endEdit();
40650         return this;
40651     },
40652
40653     /**
40654      * Loads an Roo.data.Record into this form.
40655      * @param {Record} record The record to load
40656      * @return {BasicForm} this
40657      */
40658     loadRecord : function(record){
40659         this.setValues(record.data);
40660         return this;
40661     },
40662
40663     // private
40664     beforeAction : function(action){
40665         var o = action.options;
40666         
40667        
40668         if(this.waitMsgTarget === true){
40669             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
40670         }else if(this.waitMsgTarget){
40671             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40672             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
40673         }else {
40674             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
40675         }
40676          
40677     },
40678
40679     // private
40680     afterAction : function(action, success){
40681         this.activeAction = null;
40682         var o = action.options;
40683         
40684         if(this.waitMsgTarget === true){
40685             this.el.unmask();
40686         }else if(this.waitMsgTarget){
40687             this.waitMsgTarget.unmask();
40688         }else{
40689             Roo.MessageBox.updateProgress(1);
40690             Roo.MessageBox.hide();
40691         }
40692          
40693         if(success){
40694             if(o.reset){
40695                 this.reset();
40696             }
40697             Roo.callback(o.success, o.scope, [this, action]);
40698             this.fireEvent('actioncomplete', this, action);
40699             
40700         }else{
40701             Roo.callback(o.failure, o.scope, [this, action]);
40702             // show an error message if no failed handler is set..
40703             if (!this.hasListener('actionfailed')) {
40704                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
40705             }
40706             
40707             this.fireEvent('actionfailed', this, action);
40708         }
40709         
40710     },
40711
40712     /**
40713      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40714      * @param {String} id The value to search for
40715      * @return Field
40716      */
40717     findField : function(id){
40718         var field = this.items.get(id);
40719         if(!field){
40720             this.items.each(function(f){
40721                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40722                     field = f;
40723                     return false;
40724                 }
40725             });
40726         }
40727         return field || null;
40728     },
40729
40730     /**
40731      * Add a secondary form to this one, 
40732      * Used to provide tabbed forms. One form is primary, with hidden values 
40733      * which mirror the elements from the other forms.
40734      * 
40735      * @param {Roo.form.Form} form to add.
40736      * 
40737      */
40738     addForm : function(form)
40739     {
40740        
40741         if (this.childForms.indexOf(form) > -1) {
40742             // already added..
40743             return;
40744         }
40745         this.childForms.push(form);
40746         var n = '';
40747         Roo.each(form.allItems, function (fe) {
40748             
40749             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40750             if (this.findField(n)) { // already added..
40751                 return;
40752             }
40753             var add = new Roo.form.Hidden({
40754                 name : n
40755             });
40756             add.render(this.el);
40757             
40758             this.add( add );
40759         }, this);
40760         
40761     },
40762     /**
40763      * Mark fields in this form invalid in bulk.
40764      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40765      * @return {BasicForm} this
40766      */
40767     markInvalid : function(errors){
40768         if(errors instanceof Array){
40769             for(var i = 0, len = errors.length; i < len; i++){
40770                 var fieldError = errors[i];
40771                 var f = this.findField(fieldError.id);
40772                 if(f){
40773                     f.markInvalid(fieldError.msg);
40774                 }
40775             }
40776         }else{
40777             var field, id;
40778             for(id in errors){
40779                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40780                     field.markInvalid(errors[id]);
40781                 }
40782             }
40783         }
40784         Roo.each(this.childForms || [], function (f) {
40785             f.markInvalid(errors);
40786         });
40787         
40788         return this;
40789     },
40790
40791     /**
40792      * Set values for fields in this form in bulk.
40793      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40794      * @return {BasicForm} this
40795      */
40796     setValues : function(values){
40797         if(values instanceof Array){ // array of objects
40798             for(var i = 0, len = values.length; i < len; i++){
40799                 var v = values[i];
40800                 var f = this.findField(v.id);
40801                 if(f){
40802                     f.setValue(v.value);
40803                     if(this.trackResetOnLoad){
40804                         f.originalValue = f.getValue();
40805                     }
40806                 }
40807             }
40808         }else{ // object hash
40809             var field, id;
40810             for(id in values){
40811                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40812                     
40813                     if (field.setFromData && 
40814                         field.valueField && 
40815                         field.displayField &&
40816                         // combos' with local stores can 
40817                         // be queried via setValue()
40818                         // to set their value..
40819                         (field.store && !field.store.isLocal)
40820                         ) {
40821                         // it's a combo
40822                         var sd = { };
40823                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40824                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40825                         field.setFromData(sd);
40826                         
40827                     } else {
40828                         field.setValue(values[id]);
40829                     }
40830                     
40831                     
40832                     if(this.trackResetOnLoad){
40833                         field.originalValue = field.getValue();
40834                     }
40835                 }
40836             }
40837         }
40838          
40839         Roo.each(this.childForms || [], function (f) {
40840             f.setValues(values);
40841         });
40842                 
40843         return this;
40844     },
40845
40846     /**
40847      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40848      * they are returned as an array.
40849      * @param {Boolean} asString
40850      * @return {Object}
40851      */
40852     getValues : function(asString){
40853         if (this.childForms) {
40854             // copy values from the child forms
40855             Roo.each(this.childForms, function (f) {
40856                 this.setValues(f.getValues());
40857             }, this);
40858         }
40859         
40860         
40861         
40862         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40863         if(asString === true){
40864             return fs;
40865         }
40866         return Roo.urlDecode(fs);
40867     },
40868     
40869     /**
40870      * Returns the fields in this form as an object with key/value pairs. 
40871      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40872      * @return {Object}
40873      */
40874     getFieldValues : function()
40875     {
40876         if (this.childForms) {
40877             // copy values from the child forms
40878             Roo.each(this.childForms, function (f) {
40879                 this.setValues(f.getValues());
40880             }, this);
40881         }
40882         
40883         var ret = {};
40884         this.items.each(function(f){
40885             if (!f.getName()) {
40886                 return;
40887             }
40888             var v = f.getValue();
40889             if ((typeof(v) == 'object') && f.getRawValue) {
40890                 v = f.getRawValue() ; // dates..
40891             }
40892             ret[f.getName()] = v;
40893         });
40894         
40895         return ret;
40896     },
40897
40898     /**
40899      * Clears all invalid messages in this form.
40900      * @return {BasicForm} this
40901      */
40902     clearInvalid : function(){
40903         this.items.each(function(f){
40904            f.clearInvalid();
40905         });
40906         
40907         Roo.each(this.childForms || [], function (f) {
40908             f.clearInvalid();
40909         });
40910         
40911         
40912         return this;
40913     },
40914
40915     /**
40916      * Resets this form.
40917      * @return {BasicForm} this
40918      */
40919     reset : function(){
40920         this.items.each(function(f){
40921             f.reset();
40922         });
40923         
40924         Roo.each(this.childForms || [], function (f) {
40925             f.reset();
40926         });
40927        
40928         
40929         return this;
40930     },
40931
40932     /**
40933      * Add Roo.form components to this form.
40934      * @param {Field} field1
40935      * @param {Field} field2 (optional)
40936      * @param {Field} etc (optional)
40937      * @return {BasicForm} this
40938      */
40939     add : function(){
40940         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40941         return this;
40942     },
40943
40944
40945     /**
40946      * Removes a field from the items collection (does NOT remove its markup).
40947      * @param {Field} field
40948      * @return {BasicForm} this
40949      */
40950     remove : function(field){
40951         this.items.remove(field);
40952         return this;
40953     },
40954
40955     /**
40956      * Looks at the fields in this form, checks them for an id attribute,
40957      * and calls applyTo on the existing dom element with that id.
40958      * @return {BasicForm} this
40959      */
40960     render : function(){
40961         this.items.each(function(f){
40962             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40963                 f.applyTo(f.id);
40964             }
40965         });
40966         return this;
40967     },
40968
40969     /**
40970      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40971      * @param {Object} values
40972      * @return {BasicForm} this
40973      */
40974     applyToFields : function(o){
40975         this.items.each(function(f){
40976            Roo.apply(f, o);
40977         });
40978         return this;
40979     },
40980
40981     /**
40982      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40983      * @param {Object} values
40984      * @return {BasicForm} this
40985      */
40986     applyIfToFields : function(o){
40987         this.items.each(function(f){
40988            Roo.applyIf(f, o);
40989         });
40990         return this;
40991     }
40992 });
40993
40994 // back compat
40995 Roo.BasicForm = Roo.form.BasicForm;/*
40996  * Based on:
40997  * Ext JS Library 1.1.1
40998  * Copyright(c) 2006-2007, Ext JS, LLC.
40999  *
41000  * Originally Released Under LGPL - original licence link has changed is not relivant.
41001  *
41002  * Fork - LGPL
41003  * <script type="text/javascript">
41004  */
41005
41006 /**
41007  * @class Roo.form.Form
41008  * @extends Roo.form.BasicForm
41009  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41010  * @constructor
41011  * @param {Object} config Configuration options
41012  */
41013 Roo.form.Form = function(config){
41014     var xitems =  [];
41015     if (config.items) {
41016         xitems = config.items;
41017         delete config.items;
41018     }
41019    
41020     
41021     Roo.form.Form.superclass.constructor.call(this, null, config);
41022     this.url = this.url || this.action;
41023     if(!this.root){
41024         this.root = new Roo.form.Layout(Roo.applyIf({
41025             id: Roo.id()
41026         }, config));
41027     }
41028     this.active = this.root;
41029     /**
41030      * Array of all the buttons that have been added to this form via {@link addButton}
41031      * @type Array
41032      */
41033     this.buttons = [];
41034     this.allItems = [];
41035     this.addEvents({
41036         /**
41037          * @event clientvalidation
41038          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41039          * @param {Form} this
41040          * @param {Boolean} valid true if the form has passed client-side validation
41041          */
41042         clientvalidation: true,
41043         /**
41044          * @event rendered
41045          * Fires when the form is rendered
41046          * @param {Roo.form.Form} form
41047          */
41048         rendered : true
41049     });
41050     
41051     if (this.progressUrl) {
41052             // push a hidden field onto the list of fields..
41053             this.addxtype( {
41054                     xns: Roo.form, 
41055                     xtype : 'Hidden', 
41056                     name : 'UPLOAD_IDENTIFIER' 
41057             });
41058         }
41059         
41060     
41061     Roo.each(xitems, this.addxtype, this);
41062     
41063     
41064     
41065 };
41066
41067 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41068     /**
41069      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41070      */
41071     /**
41072      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41073      */
41074     /**
41075      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41076      */
41077     buttonAlign:'center',
41078
41079     /**
41080      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41081      */
41082     minButtonWidth:75,
41083
41084     /**
41085      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41086      * This property cascades to child containers if not set.
41087      */
41088     labelAlign:'left',
41089
41090     /**
41091      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41092      * fires a looping event with that state. This is required to bind buttons to the valid
41093      * state using the config value formBind:true on the button.
41094      */
41095     monitorValid : false,
41096
41097     /**
41098      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41099      */
41100     monitorPoll : 200,
41101     
41102     /**
41103      * @cfg {String} progressUrl - Url to return progress data 
41104      */
41105     
41106     progressUrl : false,
41107   
41108     /**
41109      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41110      * fields are added and the column is closed. If no fields are passed the column remains open
41111      * until end() is called.
41112      * @param {Object} config The config to pass to the column
41113      * @param {Field} field1 (optional)
41114      * @param {Field} field2 (optional)
41115      * @param {Field} etc (optional)
41116      * @return Column The column container object
41117      */
41118     column : function(c){
41119         var col = new Roo.form.Column(c);
41120         this.start(col);
41121         if(arguments.length > 1){ // duplicate code required because of Opera
41122             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41123             this.end();
41124         }
41125         return col;
41126     },
41127
41128     /**
41129      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41130      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41131      * until end() is called.
41132      * @param {Object} config The config to pass to the fieldset
41133      * @param {Field} field1 (optional)
41134      * @param {Field} field2 (optional)
41135      * @param {Field} etc (optional)
41136      * @return FieldSet The fieldset container object
41137      */
41138     fieldset : function(c){
41139         var fs = new Roo.form.FieldSet(c);
41140         this.start(fs);
41141         if(arguments.length > 1){ // duplicate code required because of Opera
41142             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41143             this.end();
41144         }
41145         return fs;
41146     },
41147
41148     /**
41149      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41150      * fields are added and the container is closed. If no fields are passed the container remains open
41151      * until end() is called.
41152      * @param {Object} config The config to pass to the Layout
41153      * @param {Field} field1 (optional)
41154      * @param {Field} field2 (optional)
41155      * @param {Field} etc (optional)
41156      * @return Layout The container object
41157      */
41158     container : function(c){
41159         var l = new Roo.form.Layout(c);
41160         this.start(l);
41161         if(arguments.length > 1){ // duplicate code required because of Opera
41162             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41163             this.end();
41164         }
41165         return l;
41166     },
41167
41168     /**
41169      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41170      * @param {Object} container A Roo.form.Layout or subclass of Layout
41171      * @return {Form} this
41172      */
41173     start : function(c){
41174         // cascade label info
41175         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41176         this.active.stack.push(c);
41177         c.ownerCt = this.active;
41178         this.active = c;
41179         return this;
41180     },
41181
41182     /**
41183      * Closes the current open container
41184      * @return {Form} this
41185      */
41186     end : function(){
41187         if(this.active == this.root){
41188             return this;
41189         }
41190         this.active = this.active.ownerCt;
41191         return this;
41192     },
41193
41194     /**
41195      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41196      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41197      * as the label of the field.
41198      * @param {Field} field1
41199      * @param {Field} field2 (optional)
41200      * @param {Field} etc. (optional)
41201      * @return {Form} this
41202      */
41203     add : function(){
41204         this.active.stack.push.apply(this.active.stack, arguments);
41205         this.allItems.push.apply(this.allItems,arguments);
41206         var r = [];
41207         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41208             if(a[i].isFormField){
41209                 r.push(a[i]);
41210             }
41211         }
41212         if(r.length > 0){
41213             Roo.form.Form.superclass.add.apply(this, r);
41214         }
41215         return this;
41216     },
41217     
41218
41219     
41220     
41221     
41222      /**
41223      * Find any element that has been added to a form, using it's ID or name
41224      * This can include framesets, columns etc. along with regular fields..
41225      * @param {String} id - id or name to find.
41226      
41227      * @return {Element} e - or false if nothing found.
41228      */
41229     findbyId : function(id)
41230     {
41231         var ret = false;
41232         if (!id) {
41233             return ret;
41234         }
41235         Roo.each(this.allItems, function(f){
41236             if (f.id == id || f.name == id ){
41237                 ret = f;
41238                 return false;
41239             }
41240         });
41241         return ret;
41242     },
41243
41244     
41245     
41246     /**
41247      * Render this form into the passed container. This should only be called once!
41248      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41249      * @return {Form} this
41250      */
41251     render : function(ct)
41252     {
41253         
41254         
41255         
41256         ct = Roo.get(ct);
41257         var o = this.autoCreate || {
41258             tag: 'form',
41259             method : this.method || 'POST',
41260             id : this.id || Roo.id()
41261         };
41262         this.initEl(ct.createChild(o));
41263
41264         this.root.render(this.el);
41265         
41266        
41267              
41268         this.items.each(function(f){
41269             f.render('x-form-el-'+f.id);
41270         });
41271
41272         if(this.buttons.length > 0){
41273             // tables are required to maintain order and for correct IE layout
41274             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41275                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41276                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41277             }}, null, true);
41278             var tr = tb.getElementsByTagName('tr')[0];
41279             for(var i = 0, len = this.buttons.length; i < len; i++) {
41280                 var b = this.buttons[i];
41281                 var td = document.createElement('td');
41282                 td.className = 'x-form-btn-td';
41283                 b.render(tr.appendChild(td));
41284             }
41285         }
41286         if(this.monitorValid){ // initialize after render
41287             this.startMonitoring();
41288         }
41289         this.fireEvent('rendered', this);
41290         return this;
41291     },
41292
41293     /**
41294      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41295      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41296      * object or a valid Roo.DomHelper element config
41297      * @param {Function} handler The function called when the button is clicked
41298      * @param {Object} scope (optional) The scope of the handler function
41299      * @return {Roo.Button}
41300      */
41301     addButton : function(config, handler, scope){
41302         var bc = {
41303             handler: handler,
41304             scope: scope,
41305             minWidth: this.minButtonWidth,
41306             hideParent:true
41307         };
41308         if(typeof config == "string"){
41309             bc.text = config;
41310         }else{
41311             Roo.apply(bc, config);
41312         }
41313         var btn = new Roo.Button(null, bc);
41314         this.buttons.push(btn);
41315         return btn;
41316     },
41317
41318      /**
41319      * Adds a series of form elements (using the xtype property as the factory method.
41320      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41321      * @param {Object} config 
41322      */
41323     
41324     addxtype : function()
41325     {
41326         var ar = Array.prototype.slice.call(arguments, 0);
41327         var ret = false;
41328         for(var i = 0; i < ar.length; i++) {
41329             if (!ar[i]) {
41330                 continue; // skip -- if this happends something invalid got sent, we 
41331                 // should ignore it, as basically that interface element will not show up
41332                 // and that should be pretty obvious!!
41333             }
41334             
41335             if (Roo.form[ar[i].xtype]) {
41336                 ar[i].form = this;
41337                 var fe = Roo.factory(ar[i], Roo.form);
41338                 if (!ret) {
41339                     ret = fe;
41340                 }
41341                 fe.form = this;
41342                 if (fe.store) {
41343                     fe.store.form = this;
41344                 }
41345                 if (fe.isLayout) {  
41346                          
41347                     this.start(fe);
41348                     this.allItems.push(fe);
41349                     if (fe.items && fe.addxtype) {
41350                         fe.addxtype.apply(fe, fe.items);
41351                         delete fe.items;
41352                     }
41353                      this.end();
41354                     continue;
41355                 }
41356                 
41357                 
41358                  
41359                 this.add(fe);
41360               //  console.log('adding ' + ar[i].xtype);
41361             }
41362             if (ar[i].xtype == 'Button') {  
41363                 //console.log('adding button');
41364                 //console.log(ar[i]);
41365                 this.addButton(ar[i]);
41366                 this.allItems.push(fe);
41367                 continue;
41368             }
41369             
41370             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41371                 alert('end is not supported on xtype any more, use items');
41372             //    this.end();
41373             //    //console.log('adding end');
41374             }
41375             
41376         }
41377         return ret;
41378     },
41379     
41380     /**
41381      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41382      * option "monitorValid"
41383      */
41384     startMonitoring : function(){
41385         if(!this.bound){
41386             this.bound = true;
41387             Roo.TaskMgr.start({
41388                 run : this.bindHandler,
41389                 interval : this.monitorPoll || 200,
41390                 scope: this
41391             });
41392         }
41393     },
41394
41395     /**
41396      * Stops monitoring of the valid state of this form
41397      */
41398     stopMonitoring : function(){
41399         this.bound = false;
41400     },
41401
41402     // private
41403     bindHandler : function(){
41404         if(!this.bound){
41405             return false; // stops binding
41406         }
41407         var valid = true;
41408         this.items.each(function(f){
41409             if(!f.isValid(true)){
41410                 valid = false;
41411                 return false;
41412             }
41413         });
41414         for(var i = 0, len = this.buttons.length; i < len; i++){
41415             var btn = this.buttons[i];
41416             if(btn.formBind === true && btn.disabled === valid){
41417                 btn.setDisabled(!valid);
41418             }
41419         }
41420         this.fireEvent('clientvalidation', this, valid);
41421     }
41422     
41423     
41424     
41425     
41426     
41427     
41428     
41429     
41430 });
41431
41432
41433 // back compat
41434 Roo.Form = Roo.form.Form;
41435 /*
41436  * Based on:
41437  * Ext JS Library 1.1.1
41438  * Copyright(c) 2006-2007, Ext JS, LLC.
41439  *
41440  * Originally Released Under LGPL - original licence link has changed is not relivant.
41441  *
41442  * Fork - LGPL
41443  * <script type="text/javascript">
41444  */
41445  
41446  /**
41447  * @class Roo.form.Action
41448  * Internal Class used to handle form actions
41449  * @constructor
41450  * @param {Roo.form.BasicForm} el The form element or its id
41451  * @param {Object} config Configuration options
41452  */
41453  
41454  
41455 // define the action interface
41456 Roo.form.Action = function(form, options){
41457     this.form = form;
41458     this.options = options || {};
41459 };
41460 /**
41461  * Client Validation Failed
41462  * @const 
41463  */
41464 Roo.form.Action.CLIENT_INVALID = 'client';
41465 /**
41466  * Server Validation Failed
41467  * @const 
41468  */
41469  Roo.form.Action.SERVER_INVALID = 'server';
41470  /**
41471  * Connect to Server Failed
41472  * @const 
41473  */
41474 Roo.form.Action.CONNECT_FAILURE = 'connect';
41475 /**
41476  * Reading Data from Server Failed
41477  * @const 
41478  */
41479 Roo.form.Action.LOAD_FAILURE = 'load';
41480
41481 Roo.form.Action.prototype = {
41482     type : 'default',
41483     failureType : undefined,
41484     response : undefined,
41485     result : undefined,
41486
41487     // interface method
41488     run : function(options){
41489
41490     },
41491
41492     // interface method
41493     success : function(response){
41494
41495     },
41496
41497     // interface method
41498     handleResponse : function(response){
41499
41500     },
41501
41502     // default connection failure
41503     failure : function(response){
41504         
41505         this.response = response;
41506         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41507         this.form.afterAction(this, false);
41508     },
41509
41510     processResponse : function(response){
41511         this.response = response;
41512         if(!response.responseText){
41513             return true;
41514         }
41515         this.result = this.handleResponse(response);
41516         return this.result;
41517     },
41518
41519     // utility functions used internally
41520     getUrl : function(appendParams){
41521         var url = this.options.url || this.form.url || this.form.el.dom.action;
41522         if(appendParams){
41523             var p = this.getParams();
41524             if(p){
41525                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41526             }
41527         }
41528         return url;
41529     },
41530
41531     getMethod : function(){
41532         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41533     },
41534
41535     getParams : function(){
41536         var bp = this.form.baseParams;
41537         var p = this.options.params;
41538         if(p){
41539             if(typeof p == "object"){
41540                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41541             }else if(typeof p == 'string' && bp){
41542                 p += '&' + Roo.urlEncode(bp);
41543             }
41544         }else if(bp){
41545             p = Roo.urlEncode(bp);
41546         }
41547         return p;
41548     },
41549
41550     createCallback : function(){
41551         return {
41552             success: this.success,
41553             failure: this.failure,
41554             scope: this,
41555             timeout: (this.form.timeout*1000),
41556             upload: this.form.fileUpload ? this.success : undefined
41557         };
41558     }
41559 };
41560
41561 Roo.form.Action.Submit = function(form, options){
41562     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41563 };
41564
41565 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41566     type : 'submit',
41567
41568     haveProgress : false,
41569     uploadComplete : false,
41570     
41571     // uploadProgress indicator.
41572     uploadProgress : function()
41573     {
41574         if (!this.form.progressUrl) {
41575             return;
41576         }
41577         
41578         if (!this.haveProgress) {
41579             Roo.MessageBox.progress("Uploading", "Uploading");
41580         }
41581         if (this.uploadComplete) {
41582            Roo.MessageBox.hide();
41583            return;
41584         }
41585         
41586         this.haveProgress = true;
41587    
41588         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41589         
41590         var c = new Roo.data.Connection();
41591         c.request({
41592             url : this.form.progressUrl,
41593             params: {
41594                 id : uid
41595             },
41596             method: 'GET',
41597             success : function(req){
41598                //console.log(data);
41599                 var rdata = false;
41600                 var edata;
41601                 try  {
41602                    rdata = Roo.decode(req.responseText)
41603                 } catch (e) {
41604                     Roo.log("Invalid data from server..");
41605                     Roo.log(edata);
41606                     return;
41607                 }
41608                 if (!rdata || !rdata.success) {
41609                     Roo.log(rdata);
41610                     return;
41611                 }
41612                 var data = rdata.data;
41613                 
41614                 if (this.uploadComplete) {
41615                    Roo.MessageBox.hide();
41616                    return;
41617                 }
41618                    
41619                 if (data){
41620                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41621                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41622                     );
41623                 }
41624                 this.uploadProgress.defer(2000,this);
41625             },
41626        
41627             failure: function(data) {
41628                 Roo.log('progress url failed ');
41629                 Roo.log(data);
41630             },
41631             scope : this
41632         });
41633            
41634     },
41635     
41636     
41637     run : function()
41638     {
41639         // run get Values on the form, so it syncs any secondary forms.
41640         this.form.getValues();
41641         
41642         var o = this.options;
41643         var method = this.getMethod();
41644         var isPost = method == 'POST';
41645         if(o.clientValidation === false || this.form.isValid()){
41646             
41647             if (this.form.progressUrl) {
41648                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41649                     (new Date() * 1) + '' + Math.random());
41650                     
41651             } 
41652             
41653             
41654             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41655                 form:this.form.el.dom,
41656                 url:this.getUrl(!isPost),
41657                 method: method,
41658                 params:isPost ? this.getParams() : null,
41659                 isUpload: this.form.fileUpload
41660             }));
41661             
41662             this.uploadProgress();
41663
41664         }else if (o.clientValidation !== false){ // client validation failed
41665             this.failureType = Roo.form.Action.CLIENT_INVALID;
41666             this.form.afterAction(this, false);
41667         }
41668     },
41669
41670     success : function(response)
41671     {
41672         this.uploadComplete= true;
41673         if (this.haveProgress) {
41674             Roo.MessageBox.hide();
41675         }
41676         
41677         
41678         var result = this.processResponse(response);
41679         if(result === true || result.success){
41680             this.form.afterAction(this, true);
41681             return;
41682         }
41683         if(result.errors){
41684             this.form.markInvalid(result.errors);
41685             this.failureType = Roo.form.Action.SERVER_INVALID;
41686         }
41687         this.form.afterAction(this, false);
41688     },
41689     failure : function(response)
41690     {
41691         this.uploadComplete= true;
41692         if (this.haveProgress) {
41693             Roo.MessageBox.hide();
41694         }
41695         
41696         
41697         this.response = response;
41698         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41699         this.form.afterAction(this, false);
41700     },
41701     
41702     handleResponse : function(response){
41703         if(this.form.errorReader){
41704             var rs = this.form.errorReader.read(response);
41705             var errors = [];
41706             if(rs.records){
41707                 for(var i = 0, len = rs.records.length; i < len; i++) {
41708                     var r = rs.records[i];
41709                     errors[i] = r.data;
41710                 }
41711             }
41712             if(errors.length < 1){
41713                 errors = null;
41714             }
41715             return {
41716                 success : rs.success,
41717                 errors : errors
41718             };
41719         }
41720         var ret = false;
41721         try {
41722             ret = Roo.decode(response.responseText);
41723         } catch (e) {
41724             ret = {
41725                 success: false,
41726                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41727                 errors : []
41728             };
41729         }
41730         return ret;
41731         
41732     }
41733 });
41734
41735
41736 Roo.form.Action.Load = function(form, options){
41737     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41738     this.reader = this.form.reader;
41739 };
41740
41741 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41742     type : 'load',
41743
41744     run : function(){
41745         
41746         Roo.Ajax.request(Roo.apply(
41747                 this.createCallback(), {
41748                     method:this.getMethod(),
41749                     url:this.getUrl(false),
41750                     params:this.getParams()
41751         }));
41752     },
41753
41754     success : function(response){
41755         
41756         var result = this.processResponse(response);
41757         if(result === true || !result.success || !result.data){
41758             this.failureType = Roo.form.Action.LOAD_FAILURE;
41759             this.form.afterAction(this, false);
41760             return;
41761         }
41762         this.form.clearInvalid();
41763         this.form.setValues(result.data);
41764         this.form.afterAction(this, true);
41765     },
41766
41767     handleResponse : function(response){
41768         if(this.form.reader){
41769             var rs = this.form.reader.read(response);
41770             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41771             return {
41772                 success : rs.success,
41773                 data : data
41774             };
41775         }
41776         return Roo.decode(response.responseText);
41777     }
41778 });
41779
41780 Roo.form.Action.ACTION_TYPES = {
41781     'load' : Roo.form.Action.Load,
41782     'submit' : Roo.form.Action.Submit
41783 };/*
41784  * Based on:
41785  * Ext JS Library 1.1.1
41786  * Copyright(c) 2006-2007, Ext JS, LLC.
41787  *
41788  * Originally Released Under LGPL - original licence link has changed is not relivant.
41789  *
41790  * Fork - LGPL
41791  * <script type="text/javascript">
41792  */
41793  
41794 /**
41795  * @class Roo.form.Layout
41796  * @extends Roo.Component
41797  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41798  * @constructor
41799  * @param {Object} config Configuration options
41800  */
41801 Roo.form.Layout = function(config){
41802     var xitems = [];
41803     if (config.items) {
41804         xitems = config.items;
41805         delete config.items;
41806     }
41807     Roo.form.Layout.superclass.constructor.call(this, config);
41808     this.stack = [];
41809     Roo.each(xitems, this.addxtype, this);
41810      
41811 };
41812
41813 Roo.extend(Roo.form.Layout, Roo.Component, {
41814     /**
41815      * @cfg {String/Object} autoCreate
41816      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41817      */
41818     /**
41819      * @cfg {String/Object/Function} style
41820      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41821      * a function which returns such a specification.
41822      */
41823     /**
41824      * @cfg {String} labelAlign
41825      * Valid values are "left," "top" and "right" (defaults to "left")
41826      */
41827     /**
41828      * @cfg {Number} labelWidth
41829      * Fixed width in pixels of all field labels (defaults to undefined)
41830      */
41831     /**
41832      * @cfg {Boolean} clear
41833      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41834      */
41835     clear : true,
41836     /**
41837      * @cfg {String} labelSeparator
41838      * The separator to use after field labels (defaults to ':')
41839      */
41840     labelSeparator : ':',
41841     /**
41842      * @cfg {Boolean} hideLabels
41843      * True to suppress the display of field labels in this layout (defaults to false)
41844      */
41845     hideLabels : false,
41846
41847     // private
41848     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41849     
41850     isLayout : true,
41851     
41852     // private
41853     onRender : function(ct, position){
41854         if(this.el){ // from markup
41855             this.el = Roo.get(this.el);
41856         }else {  // generate
41857             var cfg = this.getAutoCreate();
41858             this.el = ct.createChild(cfg, position);
41859         }
41860         if(this.style){
41861             this.el.applyStyles(this.style);
41862         }
41863         if(this.labelAlign){
41864             this.el.addClass('x-form-label-'+this.labelAlign);
41865         }
41866         if(this.hideLabels){
41867             this.labelStyle = "display:none";
41868             this.elementStyle = "padding-left:0;";
41869         }else{
41870             if(typeof this.labelWidth == 'number'){
41871                 this.labelStyle = "width:"+this.labelWidth+"px;";
41872                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41873             }
41874             if(this.labelAlign == 'top'){
41875                 this.labelStyle = "width:auto;";
41876                 this.elementStyle = "padding-left:0;";
41877             }
41878         }
41879         var stack = this.stack;
41880         var slen = stack.length;
41881         if(slen > 0){
41882             if(!this.fieldTpl){
41883                 var t = new Roo.Template(
41884                     '<div class="x-form-item {5}">',
41885                         '<label for="{0}" style="{2}">{1}{4}</label>',
41886                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41887                         '</div>',
41888                     '</div><div class="x-form-clear-left"></div>'
41889                 );
41890                 t.disableFormats = true;
41891                 t.compile();
41892                 Roo.form.Layout.prototype.fieldTpl = t;
41893             }
41894             for(var i = 0; i < slen; i++) {
41895                 if(stack[i].isFormField){
41896                     this.renderField(stack[i]);
41897                 }else{
41898                     this.renderComponent(stack[i]);
41899                 }
41900             }
41901         }
41902         if(this.clear){
41903             this.el.createChild({cls:'x-form-clear'});
41904         }
41905     },
41906
41907     // private
41908     renderField : function(f){
41909         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41910                f.id, //0
41911                f.fieldLabel, //1
41912                f.labelStyle||this.labelStyle||'', //2
41913                this.elementStyle||'', //3
41914                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41915                f.itemCls||this.itemCls||''  //5
41916        ], true).getPrevSibling());
41917     },
41918
41919     // private
41920     renderComponent : function(c){
41921         c.render(c.isLayout ? this.el : this.el.createChild());    
41922     },
41923     /**
41924      * Adds a object form elements (using the xtype property as the factory method.)
41925      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41926      * @param {Object} config 
41927      */
41928     addxtype : function(o)
41929     {
41930         // create the lement.
41931         o.form = this.form;
41932         var fe = Roo.factory(o, Roo.form);
41933         this.form.allItems.push(fe);
41934         this.stack.push(fe);
41935         
41936         if (fe.isFormField) {
41937             this.form.items.add(fe);
41938         }
41939          
41940         return fe;
41941     }
41942 });
41943
41944 /**
41945  * @class Roo.form.Column
41946  * @extends Roo.form.Layout
41947  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41948  * @constructor
41949  * @param {Object} config Configuration options
41950  */
41951 Roo.form.Column = function(config){
41952     Roo.form.Column.superclass.constructor.call(this, config);
41953 };
41954
41955 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41956     /**
41957      * @cfg {Number/String} width
41958      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41959      */
41960     /**
41961      * @cfg {String/Object} autoCreate
41962      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41963      */
41964
41965     // private
41966     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41967
41968     // private
41969     onRender : function(ct, position){
41970         Roo.form.Column.superclass.onRender.call(this, ct, position);
41971         if(this.width){
41972             this.el.setWidth(this.width);
41973         }
41974     }
41975 });
41976
41977
41978 /**
41979  * @class Roo.form.Row
41980  * @extends Roo.form.Layout
41981  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41982  * @constructor
41983  * @param {Object} config Configuration options
41984  */
41985
41986  
41987 Roo.form.Row = function(config){
41988     Roo.form.Row.superclass.constructor.call(this, config);
41989 };
41990  
41991 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41992       /**
41993      * @cfg {Number/String} width
41994      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41995      */
41996     /**
41997      * @cfg {Number/String} height
41998      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41999      */
42000     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42001     
42002     padWidth : 20,
42003     // private
42004     onRender : function(ct, position){
42005         //console.log('row render');
42006         if(!this.rowTpl){
42007             var t = new Roo.Template(
42008                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42009                     '<label for="{0}" style="{2}">{1}{4}</label>',
42010                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42011                     '</div>',
42012                 '</div>'
42013             );
42014             t.disableFormats = true;
42015             t.compile();
42016             Roo.form.Layout.prototype.rowTpl = t;
42017         }
42018         this.fieldTpl = this.rowTpl;
42019         
42020         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42021         var labelWidth = 100;
42022         
42023         if ((this.labelAlign != 'top')) {
42024             if (typeof this.labelWidth == 'number') {
42025                 labelWidth = this.labelWidth
42026             }
42027             this.padWidth =  20 + labelWidth;
42028             
42029         }
42030         
42031         Roo.form.Column.superclass.onRender.call(this, ct, position);
42032         if(this.width){
42033             this.el.setWidth(this.width);
42034         }
42035         if(this.height){
42036             this.el.setHeight(this.height);
42037         }
42038     },
42039     
42040     // private
42041     renderField : function(f){
42042         f.fieldEl = this.fieldTpl.append(this.el, [
42043                f.id, f.fieldLabel,
42044                f.labelStyle||this.labelStyle||'',
42045                this.elementStyle||'',
42046                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42047                f.itemCls||this.itemCls||'',
42048                f.width ? f.width + this.padWidth : 160 + this.padWidth
42049        ],true);
42050     }
42051 });
42052  
42053
42054 /**
42055  * @class Roo.form.FieldSet
42056  * @extends Roo.form.Layout
42057  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42058  * @constructor
42059  * @param {Object} config Configuration options
42060  */
42061 Roo.form.FieldSet = function(config){
42062     Roo.form.FieldSet.superclass.constructor.call(this, config);
42063 };
42064
42065 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42066     /**
42067      * @cfg {String} legend
42068      * The text to display as the legend for the FieldSet (defaults to '')
42069      */
42070     /**
42071      * @cfg {String/Object} autoCreate
42072      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42073      */
42074
42075     // private
42076     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42077
42078     // private
42079     onRender : function(ct, position){
42080         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42081         if(this.legend){
42082             this.setLegend(this.legend);
42083         }
42084     },
42085
42086     // private
42087     setLegend : function(text){
42088         if(this.rendered){
42089             this.el.child('legend').update(text);
42090         }
42091     }
42092 });/*
42093  * Based on:
42094  * Ext JS Library 1.1.1
42095  * Copyright(c) 2006-2007, Ext JS, LLC.
42096  *
42097  * Originally Released Under LGPL - original licence link has changed is not relivant.
42098  *
42099  * Fork - LGPL
42100  * <script type="text/javascript">
42101  */
42102 /**
42103  * @class Roo.form.VTypes
42104  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42105  * @singleton
42106  */
42107 Roo.form.VTypes = function(){
42108     // closure these in so they are only created once.
42109     var alpha = /^[a-zA-Z_]+$/;
42110     var alphanum = /^[a-zA-Z0-9_]+$/;
42111     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42112     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42113
42114     // All these messages and functions are configurable
42115     return {
42116         /**
42117          * The function used to validate email addresses
42118          * @param {String} value The email address
42119          */
42120         'email' : function(v){
42121             return email.test(v);
42122         },
42123         /**
42124          * The error text to display when the email validation function returns false
42125          * @type String
42126          */
42127         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42128         /**
42129          * The keystroke filter mask to be applied on email input
42130          * @type RegExp
42131          */
42132         'emailMask' : /[a-z0-9_\.\-@]/i,
42133
42134         /**
42135          * The function used to validate URLs
42136          * @param {String} value The URL
42137          */
42138         'url' : function(v){
42139             return url.test(v);
42140         },
42141         /**
42142          * The error text to display when the url validation function returns false
42143          * @type String
42144          */
42145         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42146         
42147         /**
42148          * The function used to validate alpha values
42149          * @param {String} value The value
42150          */
42151         'alpha' : function(v){
42152             return alpha.test(v);
42153         },
42154         /**
42155          * The error text to display when the alpha validation function returns false
42156          * @type String
42157          */
42158         'alphaText' : 'This field should only contain letters and _',
42159         /**
42160          * The keystroke filter mask to be applied on alpha input
42161          * @type RegExp
42162          */
42163         'alphaMask' : /[a-z_]/i,
42164
42165         /**
42166          * The function used to validate alphanumeric values
42167          * @param {String} value The value
42168          */
42169         'alphanum' : function(v){
42170             return alphanum.test(v);
42171         },
42172         /**
42173          * The error text to display when the alphanumeric validation function returns false
42174          * @type String
42175          */
42176         'alphanumText' : 'This field should only contain letters, numbers and _',
42177         /**
42178          * The keystroke filter mask to be applied on alphanumeric input
42179          * @type RegExp
42180          */
42181         'alphanumMask' : /[a-z0-9_]/i
42182     };
42183 }();//<script type="text/javascript">
42184
42185 /**
42186  * @class Roo.form.FCKeditor
42187  * @extends Roo.form.TextArea
42188  * Wrapper around the FCKEditor http://www.fckeditor.net
42189  * @constructor
42190  * Creates a new FCKeditor
42191  * @param {Object} config Configuration options
42192  */
42193 Roo.form.FCKeditor = function(config){
42194     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42195     this.addEvents({
42196          /**
42197          * @event editorinit
42198          * Fired when the editor is initialized - you can add extra handlers here..
42199          * @param {FCKeditor} this
42200          * @param {Object} the FCK object.
42201          */
42202         editorinit : true
42203     });
42204     
42205     
42206 };
42207 Roo.form.FCKeditor.editors = { };
42208 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42209 {
42210     //defaultAutoCreate : {
42211     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42212     //},
42213     // private
42214     /**
42215      * @cfg {Object} fck options - see fck manual for details.
42216      */
42217     fckconfig : false,
42218     
42219     /**
42220      * @cfg {Object} fck toolbar set (Basic or Default)
42221      */
42222     toolbarSet : 'Basic',
42223     /**
42224      * @cfg {Object} fck BasePath
42225      */ 
42226     basePath : '/fckeditor/',
42227     
42228     
42229     frame : false,
42230     
42231     value : '',
42232     
42233    
42234     onRender : function(ct, position)
42235     {
42236         if(!this.el){
42237             this.defaultAutoCreate = {
42238                 tag: "textarea",
42239                 style:"width:300px;height:60px;",
42240                 autocomplete: "off"
42241             };
42242         }
42243         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42244         /*
42245         if(this.grow){
42246             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42247             if(this.preventScrollbars){
42248                 this.el.setStyle("overflow", "hidden");
42249             }
42250             this.el.setHeight(this.growMin);
42251         }
42252         */
42253         //console.log('onrender' + this.getId() );
42254         Roo.form.FCKeditor.editors[this.getId()] = this;
42255          
42256
42257         this.replaceTextarea() ;
42258         
42259     },
42260     
42261     getEditor : function() {
42262         return this.fckEditor;
42263     },
42264     /**
42265      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42266      * @param {Mixed} value The value to set
42267      */
42268     
42269     
42270     setValue : function(value)
42271     {
42272         //console.log('setValue: ' + value);
42273         
42274         if(typeof(value) == 'undefined') { // not sure why this is happending...
42275             return;
42276         }
42277         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42278         
42279         //if(!this.el || !this.getEditor()) {
42280         //    this.value = value;
42281             //this.setValue.defer(100,this,[value]);    
42282         //    return;
42283         //} 
42284         
42285         if(!this.getEditor()) {
42286             return;
42287         }
42288         
42289         this.getEditor().SetData(value);
42290         
42291         //
42292
42293     },
42294
42295     /**
42296      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42297      * @return {Mixed} value The field value
42298      */
42299     getValue : function()
42300     {
42301         
42302         if (this.frame && this.frame.dom.style.display == 'none') {
42303             return Roo.form.FCKeditor.superclass.getValue.call(this);
42304         }
42305         
42306         if(!this.el || !this.getEditor()) {
42307            
42308            // this.getValue.defer(100,this); 
42309             return this.value;
42310         }
42311        
42312         
42313         var value=this.getEditor().GetData();
42314         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42315         return Roo.form.FCKeditor.superclass.getValue.call(this);
42316         
42317
42318     },
42319
42320     /**
42321      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42322      * @return {Mixed} value The field value
42323      */
42324     getRawValue : function()
42325     {
42326         if (this.frame && this.frame.dom.style.display == 'none') {
42327             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42328         }
42329         
42330         if(!this.el || !this.getEditor()) {
42331             //this.getRawValue.defer(100,this); 
42332             return this.value;
42333             return;
42334         }
42335         
42336         
42337         
42338         var value=this.getEditor().GetData();
42339         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42340         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42341          
42342     },
42343     
42344     setSize : function(w,h) {
42345         
42346         
42347         
42348         //if (this.frame && this.frame.dom.style.display == 'none') {
42349         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42350         //    return;
42351         //}
42352         //if(!this.el || !this.getEditor()) {
42353         //    this.setSize.defer(100,this, [w,h]); 
42354         //    return;
42355         //}
42356         
42357         
42358         
42359         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42360         
42361         this.frame.dom.setAttribute('width', w);
42362         this.frame.dom.setAttribute('height', h);
42363         this.frame.setSize(w,h);
42364         
42365     },
42366     
42367     toggleSourceEdit : function(value) {
42368         
42369       
42370          
42371         this.el.dom.style.display = value ? '' : 'none';
42372         this.frame.dom.style.display = value ?  'none' : '';
42373         
42374     },
42375     
42376     
42377     focus: function(tag)
42378     {
42379         if (this.frame.dom.style.display == 'none') {
42380             return Roo.form.FCKeditor.superclass.focus.call(this);
42381         }
42382         if(!this.el || !this.getEditor()) {
42383             this.focus.defer(100,this, [tag]); 
42384             return;
42385         }
42386         
42387         
42388         
42389         
42390         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42391         this.getEditor().Focus();
42392         if (tgs.length) {
42393             if (!this.getEditor().Selection.GetSelection()) {
42394                 this.focus.defer(100,this, [tag]); 
42395                 return;
42396             }
42397             
42398             
42399             var r = this.getEditor().EditorDocument.createRange();
42400             r.setStart(tgs[0],0);
42401             r.setEnd(tgs[0],0);
42402             this.getEditor().Selection.GetSelection().removeAllRanges();
42403             this.getEditor().Selection.GetSelection().addRange(r);
42404             this.getEditor().Focus();
42405         }
42406         
42407     },
42408     
42409     
42410     
42411     replaceTextarea : function()
42412     {
42413         if ( document.getElementById( this.getId() + '___Frame' ) )
42414             return ;
42415         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42416         //{
42417             // We must check the elements firstly using the Id and then the name.
42418         var oTextarea = document.getElementById( this.getId() );
42419         
42420         var colElementsByName = document.getElementsByName( this.getId() ) ;
42421          
42422         oTextarea.style.display = 'none' ;
42423
42424         if ( oTextarea.tabIndex ) {            
42425             this.TabIndex = oTextarea.tabIndex ;
42426         }
42427         
42428         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42429         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42430         this.frame = Roo.get(this.getId() + '___Frame')
42431     },
42432     
42433     _getConfigHtml : function()
42434     {
42435         var sConfig = '' ;
42436
42437         for ( var o in this.fckconfig ) {
42438             sConfig += sConfig.length > 0  ? '&amp;' : '';
42439             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42440         }
42441
42442         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42443     },
42444     
42445     
42446     _getIFrameHtml : function()
42447     {
42448         var sFile = 'fckeditor.html' ;
42449         /* no idea what this is about..
42450         try
42451         {
42452             if ( (/fcksource=true/i).test( window.top.location.search ) )
42453                 sFile = 'fckeditor.original.html' ;
42454         }
42455         catch (e) { 
42456         */
42457
42458         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42459         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42460         
42461         
42462         var html = '<iframe id="' + this.getId() +
42463             '___Frame" src="' + sLink +
42464             '" width="' + this.width +
42465             '" height="' + this.height + '"' +
42466             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42467             ' frameborder="0" scrolling="no"></iframe>' ;
42468
42469         return html ;
42470     },
42471     
42472     _insertHtmlBefore : function( html, element )
42473     {
42474         if ( element.insertAdjacentHTML )       {
42475             // IE
42476             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42477         } else { // Gecko
42478             var oRange = document.createRange() ;
42479             oRange.setStartBefore( element ) ;
42480             var oFragment = oRange.createContextualFragment( html );
42481             element.parentNode.insertBefore( oFragment, element ) ;
42482         }
42483     }
42484     
42485     
42486   
42487     
42488     
42489     
42490     
42491
42492 });
42493
42494 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42495
42496 function FCKeditor_OnComplete(editorInstance){
42497     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42498     f.fckEditor = editorInstance;
42499     //console.log("loaded");
42500     f.fireEvent('editorinit', f, editorInstance);
42501
42502   
42503
42504  
42505
42506
42507
42508
42509
42510
42511
42512
42513
42514
42515
42516
42517
42518
42519
42520 //<script type="text/javascript">
42521 /**
42522  * @class Roo.form.GridField
42523  * @extends Roo.form.Field
42524  * Embed a grid (or editable grid into a form)
42525  * STATUS ALPHA
42526  * 
42527  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42528  * it needs 
42529  * xgrid.store = Roo.data.Store
42530  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42531  * xgrid.store.reader = Roo.data.JsonReader 
42532  * 
42533  * 
42534  * @constructor
42535  * Creates a new GridField
42536  * @param {Object} config Configuration options
42537  */
42538 Roo.form.GridField = function(config){
42539     Roo.form.GridField.superclass.constructor.call(this, config);
42540      
42541 };
42542
42543 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42544     /**
42545      * @cfg {Number} width  - used to restrict width of grid..
42546      */
42547     width : 100,
42548     /**
42549      * @cfg {Number} height - used to restrict height of grid..
42550      */
42551     height : 50,
42552      /**
42553      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42554          * 
42555          *}
42556      */
42557     xgrid : false, 
42558     /**
42559      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42560      * {tag: "input", type: "checkbox", autocomplete: "off"})
42561      */
42562    // defaultAutoCreate : { tag: 'div' },
42563     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42564     /**
42565      * @cfg {String} addTitle Text to include for adding a title.
42566      */
42567     addTitle : false,
42568     //
42569     onResize : function(){
42570         Roo.form.Field.superclass.onResize.apply(this, arguments);
42571     },
42572
42573     initEvents : function(){
42574         // Roo.form.Checkbox.superclass.initEvents.call(this);
42575         // has no events...
42576        
42577     },
42578
42579
42580     getResizeEl : function(){
42581         return this.wrap;
42582     },
42583
42584     getPositionEl : function(){
42585         return this.wrap;
42586     },
42587
42588     // private
42589     onRender : function(ct, position){
42590         
42591         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42592         var style = this.style;
42593         delete this.style;
42594         
42595         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42596         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42597         this.viewEl = this.wrap.createChild({ tag: 'div' });
42598         if (style) {
42599             this.viewEl.applyStyles(style);
42600         }
42601         if (this.width) {
42602             this.viewEl.setWidth(this.width);
42603         }
42604         if (this.height) {
42605             this.viewEl.setHeight(this.height);
42606         }
42607         //if(this.inputValue !== undefined){
42608         //this.setValue(this.value);
42609         
42610         
42611         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42612         
42613         
42614         this.grid.render();
42615         this.grid.getDataSource().on('remove', this.refreshValue, this);
42616         this.grid.getDataSource().on('update', this.refreshValue, this);
42617         this.grid.on('afteredit', this.refreshValue, this);
42618  
42619     },
42620      
42621     
42622     /**
42623      * Sets the value of the item. 
42624      * @param {String} either an object  or a string..
42625      */
42626     setValue : function(v){
42627         //this.value = v;
42628         v = v || []; // empty set..
42629         // this does not seem smart - it really only affects memoryproxy grids..
42630         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42631             var ds = this.grid.getDataSource();
42632             // assumes a json reader..
42633             var data = {}
42634             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42635             ds.loadData( data);
42636         }
42637         Roo.form.GridField.superclass.setValue.call(this, v);
42638         this.refreshValue();
42639         // should load data in the grid really....
42640     },
42641     
42642     // private
42643     refreshValue: function() {
42644          var val = [];
42645         this.grid.getDataSource().each(function(r) {
42646             val.push(r.data);
42647         });
42648         this.el.dom.value = Roo.encode(val);
42649     }
42650     
42651      
42652     
42653     
42654 });/*
42655  * Based on:
42656  * Ext JS Library 1.1.1
42657  * Copyright(c) 2006-2007, Ext JS, LLC.
42658  *
42659  * Originally Released Under LGPL - original licence link has changed is not relivant.
42660  *
42661  * Fork - LGPL
42662  * <script type="text/javascript">
42663  */
42664 /**
42665  * @class Roo.form.DisplayField
42666  * @extends Roo.form.Field
42667  * A generic Field to display non-editable data.
42668  * @constructor
42669  * Creates a new Display Field item.
42670  * @param {Object} config Configuration options
42671  */
42672 Roo.form.DisplayField = function(config){
42673     Roo.form.DisplayField.superclass.constructor.call(this, config);
42674     
42675 };
42676
42677 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42678     inputType:      'hidden',
42679     allowBlank:     true,
42680     readOnly:         true,
42681     
42682  
42683     /**
42684      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42685      */
42686     focusClass : undefined,
42687     /**
42688      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42689      */
42690     fieldClass: 'x-form-field',
42691     
42692      /**
42693      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42694      */
42695     valueRenderer: undefined,
42696     
42697     width: 100,
42698     /**
42699      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42700      * {tag: "input", type: "checkbox", autocomplete: "off"})
42701      */
42702      
42703  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42704
42705     onResize : function(){
42706         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42707         
42708     },
42709
42710     initEvents : function(){
42711         // Roo.form.Checkbox.superclass.initEvents.call(this);
42712         // has no events...
42713        
42714     },
42715
42716
42717     getResizeEl : function(){
42718         return this.wrap;
42719     },
42720
42721     getPositionEl : function(){
42722         return this.wrap;
42723     },
42724
42725     // private
42726     onRender : function(ct, position){
42727         
42728         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42729         //if(this.inputValue !== undefined){
42730         this.wrap = this.el.wrap();
42731         
42732         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
42733         
42734         if (this.bodyStyle) {
42735             this.viewEl.applyStyles(this.bodyStyle);
42736         }
42737         //this.viewEl.setStyle('padding', '2px');
42738         
42739         this.setValue(this.value);
42740         
42741     },
42742 /*
42743     // private
42744     initValue : Roo.emptyFn,
42745
42746   */
42747
42748         // private
42749     onClick : function(){
42750         
42751     },
42752
42753     /**
42754      * Sets the checked state of the checkbox.
42755      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42756      */
42757     setValue : function(v){
42758         this.value = v;
42759         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42760         // this might be called before we have a dom element..
42761         if (!this.viewEl) {
42762             return;
42763         }
42764         this.viewEl.dom.innerHTML = html;
42765         Roo.form.DisplayField.superclass.setValue.call(this, v);
42766
42767     }
42768 });/*
42769  * 
42770  * Licence- LGPL
42771  * 
42772  */
42773
42774 /**
42775  * @class Roo.form.DayPicker
42776  * @extends Roo.form.Field
42777  * A Day picker show [M] [T] [W] ....
42778  * @constructor
42779  * Creates a new Day Picker
42780  * @param {Object} config Configuration options
42781  */
42782 Roo.form.DayPicker= function(config){
42783     Roo.form.DayPicker.superclass.constructor.call(this, config);
42784      
42785 };
42786
42787 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
42788     /**
42789      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42790      */
42791     focusClass : undefined,
42792     /**
42793      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42794      */
42795     fieldClass: "x-form-field",
42796    
42797     /**
42798      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42799      * {tag: "input", type: "checkbox", autocomplete: "off"})
42800      */
42801     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
42802     
42803    
42804     actionMode : 'viewEl', 
42805     //
42806     // private
42807  
42808     inputType : 'hidden',
42809     
42810      
42811     inputElement: false, // real input element?
42812     basedOn: false, // ????
42813     
42814     isFormField: true, // not sure where this is needed!!!!
42815
42816     onResize : function(){
42817         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
42818         if(!this.boxLabel){
42819             this.el.alignTo(this.wrap, 'c-c');
42820         }
42821     },
42822
42823     initEvents : function(){
42824         Roo.form.Checkbox.superclass.initEvents.call(this);
42825         this.el.on("click", this.onClick,  this);
42826         this.el.on("change", this.onClick,  this);
42827     },
42828
42829
42830     getResizeEl : function(){
42831         return this.wrap;
42832     },
42833
42834     getPositionEl : function(){
42835         return this.wrap;
42836     },
42837
42838     
42839     // private
42840     onRender : function(ct, position){
42841         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
42842        
42843         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
42844         
42845         var r1 = '<table><tr>';
42846         var r2 = '<tr class="x-form-daypick-icons">';
42847         for (var i=0; i < 7; i++) {
42848             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
42849             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
42850         }
42851         
42852         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
42853         viewEl.select('img').on('click', this.onClick, this);
42854         this.viewEl = viewEl;   
42855         
42856         
42857         // this will not work on Chrome!!!
42858         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
42859         this.el.on('propertychange', this.setFromHidden,  this);  //ie
42860         
42861         
42862           
42863
42864     },
42865
42866     // private
42867     initValue : Roo.emptyFn,
42868
42869     /**
42870      * Returns the checked state of the checkbox.
42871      * @return {Boolean} True if checked, else false
42872      */
42873     getValue : function(){
42874         return this.el.dom.value;
42875         
42876     },
42877
42878         // private
42879     onClick : function(e){ 
42880         //this.setChecked(!this.checked);
42881         Roo.get(e.target).toggleClass('x-menu-item-checked');
42882         this.refreshValue();
42883         //if(this.el.dom.checked != this.checked){
42884         //    this.setValue(this.el.dom.checked);
42885        // }
42886     },
42887     
42888     // private
42889     refreshValue : function()
42890     {
42891         var val = '';
42892         this.viewEl.select('img',true).each(function(e,i,n)  {
42893             val += e.is(".x-menu-item-checked") ? String(n) : '';
42894         });
42895         this.setValue(val, true);
42896     },
42897
42898     /**
42899      * Sets the checked state of the checkbox.
42900      * On is always based on a string comparison between inputValue and the param.
42901      * @param {Boolean/String} value - the value to set 
42902      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
42903      */
42904     setValue : function(v,suppressEvent){
42905         if (!this.el.dom) {
42906             return;
42907         }
42908         var old = this.el.dom.value ;
42909         this.el.dom.value = v;
42910         if (suppressEvent) {
42911             return ;
42912         }
42913          
42914         // update display..
42915         this.viewEl.select('img',true).each(function(e,i,n)  {
42916             
42917             var on = e.is(".x-menu-item-checked");
42918             var newv = v.indexOf(String(n)) > -1;
42919             if (on != newv) {
42920                 e.toggleClass('x-menu-item-checked');
42921             }
42922             
42923         });
42924         
42925         
42926         this.fireEvent('change', this, v, old);
42927         
42928         
42929     },
42930    
42931     // handle setting of hidden value by some other method!!?!?
42932     setFromHidden: function()
42933     {
42934         if(!this.el){
42935             return;
42936         }
42937         //console.log("SET FROM HIDDEN");
42938         //alert('setFrom hidden');
42939         this.setValue(this.el.dom.value);
42940     },
42941     
42942     onDestroy : function()
42943     {
42944         if(this.viewEl){
42945             Roo.get(this.viewEl).remove();
42946         }
42947          
42948         Roo.form.DayPicker.superclass.onDestroy.call(this);
42949     }
42950
42951 });//<script type="text/javasscript">
42952  
42953
42954 /**
42955  * @class Roo.DDView
42956  * A DnD enabled version of Roo.View.
42957  * @param {Element/String} container The Element in which to create the View.
42958  * @param {String} tpl The template string used to create the markup for each element of the View
42959  * @param {Object} config The configuration properties. These include all the config options of
42960  * {@link Roo.View} plus some specific to this class.<br>
42961  * <p>
42962  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42963  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42964  * <p>
42965  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42966 .x-view-drag-insert-above {
42967         border-top:1px dotted #3366cc;
42968 }
42969 .x-view-drag-insert-below {
42970         border-bottom:1px dotted #3366cc;
42971 }
42972 </code></pre>
42973  * 
42974  */
42975  
42976 Roo.DDView = function(container, tpl, config) {
42977     Roo.DDView.superclass.constructor.apply(this, arguments);
42978     this.getEl().setStyle("outline", "0px none");
42979     this.getEl().unselectable();
42980     if (this.dragGroup) {
42981                 this.setDraggable(this.dragGroup.split(","));
42982     }
42983     if (this.dropGroup) {
42984                 this.setDroppable(this.dropGroup.split(","));
42985     }
42986     if (this.deletable) {
42987         this.setDeletable();
42988     }
42989     this.isDirtyFlag = false;
42990         this.addEvents({
42991                 "drop" : true
42992         });
42993 };
42994
42995 Roo.extend(Roo.DDView, Roo.View, {
42996 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42997 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42998 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42999 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43000
43001         isFormField: true,
43002
43003         reset: Roo.emptyFn,
43004         
43005         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43006
43007         validate: function() {
43008                 return true;
43009         },
43010         
43011         destroy: function() {
43012                 this.purgeListeners();
43013                 this.getEl.removeAllListeners();
43014                 this.getEl().remove();
43015                 if (this.dragZone) {
43016                         if (this.dragZone.destroy) {
43017                                 this.dragZone.destroy();
43018                         }
43019                 }
43020                 if (this.dropZone) {
43021                         if (this.dropZone.destroy) {
43022                                 this.dropZone.destroy();
43023                         }
43024                 }
43025         },
43026
43027 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43028         getName: function() {
43029                 return this.name;
43030         },
43031
43032 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43033         setValue: function(v) {
43034                 if (!this.store) {
43035                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43036                 }
43037                 var data = {};
43038                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43039                 this.store.proxy = new Roo.data.MemoryProxy(data);
43040                 this.store.load();
43041         },
43042
43043 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43044         getValue: function() {
43045                 var result = '(';
43046                 this.store.each(function(rec) {
43047                         result += rec.id + ',';
43048                 });
43049                 return result.substr(0, result.length - 1) + ')';
43050         },
43051         
43052         getIds: function() {
43053                 var i = 0, result = new Array(this.store.getCount());
43054                 this.store.each(function(rec) {
43055                         result[i++] = rec.id;
43056                 });
43057                 return result;
43058         },
43059         
43060         isDirty: function() {
43061                 return this.isDirtyFlag;
43062         },
43063
43064 /**
43065  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43066  *      whole Element becomes the target, and this causes the drop gesture to append.
43067  */
43068     getTargetFromEvent : function(e) {
43069                 var target = e.getTarget();
43070                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43071                 target = target.parentNode;
43072                 }
43073                 if (!target) {
43074                         target = this.el.dom.lastChild || this.el.dom;
43075                 }
43076                 return target;
43077     },
43078
43079 /**
43080  *      Create the drag data which consists of an object which has the property "ddel" as
43081  *      the drag proxy element. 
43082  */
43083     getDragData : function(e) {
43084         var target = this.findItemFromChild(e.getTarget());
43085                 if(target) {
43086                         this.handleSelection(e);
43087                         var selNodes = this.getSelectedNodes();
43088             var dragData = {
43089                 source: this,
43090                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43091                 nodes: selNodes,
43092                 records: []
43093                         };
43094                         var selectedIndices = this.getSelectedIndexes();
43095                         for (var i = 0; i < selectedIndices.length; i++) {
43096                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43097                         }
43098                         if (selNodes.length == 1) {
43099                                 dragData.ddel = target.cloneNode(true); // the div element
43100                         } else {
43101                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43102                                 div.className = 'multi-proxy';
43103                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43104                                         div.appendChild(selNodes[i].cloneNode(true));
43105                                 }
43106                                 dragData.ddel = div;
43107                         }
43108             //console.log(dragData)
43109             //console.log(dragData.ddel.innerHTML)
43110                         return dragData;
43111                 }
43112         //console.log('nodragData')
43113                 return false;
43114     },
43115     
43116 /**     Specify to which ddGroup items in this DDView may be dragged. */
43117     setDraggable: function(ddGroup) {
43118         if (ddGroup instanceof Array) {
43119                 Roo.each(ddGroup, this.setDraggable, this);
43120                 return;
43121         }
43122         if (this.dragZone) {
43123                 this.dragZone.addToGroup(ddGroup);
43124         } else {
43125                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43126                                 containerScroll: true,
43127                                 ddGroup: ddGroup 
43128
43129                         });
43130 //                      Draggability implies selection. DragZone's mousedown selects the element.
43131                         if (!this.multiSelect) { this.singleSelect = true; }
43132
43133 //                      Wire the DragZone's handlers up to methods in *this*
43134                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43135                 }
43136     },
43137
43138 /**     Specify from which ddGroup this DDView accepts drops. */
43139     setDroppable: function(ddGroup) {
43140         if (ddGroup instanceof Array) {
43141                 Roo.each(ddGroup, this.setDroppable, this);
43142                 return;
43143         }
43144         if (this.dropZone) {
43145                 this.dropZone.addToGroup(ddGroup);
43146         } else {
43147                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43148                                 containerScroll: true,
43149                                 ddGroup: ddGroup
43150                         });
43151
43152 //                      Wire the DropZone's handlers up to methods in *this*
43153                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43154                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43155                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43156                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43157                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43158                 }
43159     },
43160
43161 /**     Decide whether to drop above or below a View node. */
43162     getDropPoint : function(e, n, dd){
43163         if (n == this.el.dom) { return "above"; }
43164                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43165                 var c = t + (b - t) / 2;
43166                 var y = Roo.lib.Event.getPageY(e);
43167                 if(y <= c) {
43168                         return "above";
43169                 }else{
43170                         return "below";
43171                 }
43172     },
43173
43174     onNodeEnter : function(n, dd, e, data){
43175                 return false;
43176     },
43177     
43178     onNodeOver : function(n, dd, e, data){
43179                 var pt = this.getDropPoint(e, n, dd);
43180                 // set the insert point style on the target node
43181                 var dragElClass = this.dropNotAllowed;
43182                 if (pt) {
43183                         var targetElClass;
43184                         if (pt == "above"){
43185                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43186                                 targetElClass = "x-view-drag-insert-above";
43187                         } else {
43188                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43189                                 targetElClass = "x-view-drag-insert-below";
43190                         }
43191                         if (this.lastInsertClass != targetElClass){
43192                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43193                                 this.lastInsertClass = targetElClass;
43194                         }
43195                 }
43196                 return dragElClass;
43197         },
43198
43199     onNodeOut : function(n, dd, e, data){
43200                 this.removeDropIndicators(n);
43201     },
43202
43203     onNodeDrop : function(n, dd, e, data){
43204         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43205                 return false;
43206         }
43207         var pt = this.getDropPoint(e, n, dd);
43208                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43209                 if (pt == "below") { insertAt++; }
43210                 for (var i = 0; i < data.records.length; i++) {
43211                         var r = data.records[i];
43212                         var dup = this.store.getById(r.id);
43213                         if (dup && (dd != this.dragZone)) {
43214                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
43215                         } else {
43216                                 if (data.copy) {
43217                                         this.store.insert(insertAt++, r.copy());
43218                                 } else {
43219                                         data.source.isDirtyFlag = true;
43220                                         r.store.remove(r);
43221                                         this.store.insert(insertAt++, r);
43222                                 }
43223                                 this.isDirtyFlag = true;
43224                         }
43225                 }
43226                 this.dragZone.cachedTarget = null;
43227                 return true;
43228     },
43229
43230     removeDropIndicators : function(n){
43231                 if(n){
43232                         Roo.fly(n).removeClass([
43233                                 "x-view-drag-insert-above",
43234                                 "x-view-drag-insert-below"]);
43235                         this.lastInsertClass = "_noclass";
43236                 }
43237     },
43238
43239 /**
43240  *      Utility method. Add a delete option to the DDView's context menu.
43241  *      @param {String} imageUrl The URL of the "delete" icon image.
43242  */
43243         setDeletable: function(imageUrl) {
43244                 if (!this.singleSelect && !this.multiSelect) {
43245                         this.singleSelect = true;
43246                 }
43247                 var c = this.getContextMenu();
43248                 this.contextMenu.on("itemclick", function(item) {
43249                         switch (item.id) {
43250                                 case "delete":
43251                                         this.remove(this.getSelectedIndexes());
43252                                         break;
43253                         }
43254                 }, this);
43255                 this.contextMenu.add({
43256                         icon: imageUrl,
43257                         id: "delete",
43258                         text: 'Delete'
43259                 });
43260         },
43261         
43262 /**     Return the context menu for this DDView. */
43263         getContextMenu: function() {
43264                 if (!this.contextMenu) {
43265 //                      Create the View's context menu
43266                         this.contextMenu = new Roo.menu.Menu({
43267                                 id: this.id + "-contextmenu"
43268                         });
43269                         this.el.on("contextmenu", this.showContextMenu, this);
43270                 }
43271                 return this.contextMenu;
43272         },
43273         
43274         disableContextMenu: function() {
43275                 if (this.contextMenu) {
43276                         this.el.un("contextmenu", this.showContextMenu, this);
43277                 }
43278         },
43279
43280         showContextMenu: function(e, item) {
43281         item = this.findItemFromChild(e.getTarget());
43282                 if (item) {
43283                         e.stopEvent();
43284                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
43285                         this.contextMenu.showAt(e.getXY());
43286             }
43287     },
43288
43289 /**
43290  *      Remove {@link Roo.data.Record}s at the specified indices.
43291  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
43292  */
43293     remove: function(selectedIndices) {
43294                 selectedIndices = [].concat(selectedIndices);
43295                 for (var i = 0; i < selectedIndices.length; i++) {
43296                         var rec = this.store.getAt(selectedIndices[i]);
43297                         this.store.remove(rec);
43298                 }
43299     },
43300
43301 /**
43302  *      Double click fires the event, but also, if this is draggable, and there is only one other
43303  *      related DropZone, it transfers the selected node.
43304  */
43305     onDblClick : function(e){
43306         var item = this.findItemFromChild(e.getTarget());
43307         if(item){
43308             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
43309                 return false;
43310             }
43311             if (this.dragGroup) {
43312                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
43313                     while (targets.indexOf(this.dropZone) > -1) {
43314                             targets.remove(this.dropZone);
43315                                 }
43316                     if (targets.length == 1) {
43317                                         this.dragZone.cachedTarget = null;
43318                         var el = Roo.get(targets[0].getEl());
43319                         var box = el.getBox(true);
43320                         targets[0].onNodeDrop(el.dom, {
43321                                 target: el.dom,
43322                                 xy: [box.x, box.y + box.height - 1]
43323                         }, null, this.getDragData(e));
43324                     }
43325                 }
43326         }
43327     },
43328     
43329     handleSelection: function(e) {
43330                 this.dragZone.cachedTarget = null;
43331         var item = this.findItemFromChild(e.getTarget());
43332         if (!item) {
43333                 this.clearSelections(true);
43334                 return;
43335         }
43336                 if (item && (this.multiSelect || this.singleSelect)){
43337                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43338                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43339                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43340                                 this.unselect(item);
43341                         } else {
43342                                 this.select(item, this.multiSelect && e.ctrlKey);
43343                                 this.lastSelection = item;
43344                         }
43345                 }
43346     },
43347
43348     onItemClick : function(item, index, e){
43349                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43350                         return false;
43351                 }
43352                 return true;
43353     },
43354
43355     unselect : function(nodeInfo, suppressEvent){
43356                 var node = this.getNode(nodeInfo);
43357                 if(node && this.isSelected(node)){
43358                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43359                                 Roo.fly(node).removeClass(this.selectedClass);
43360                                 this.selections.remove(node);
43361                                 if(!suppressEvent){
43362                                         this.fireEvent("selectionchange", this, this.selections);
43363                                 }
43364                         }
43365                 }
43366     }
43367 });
43368 /*
43369  * Based on:
43370  * Ext JS Library 1.1.1
43371  * Copyright(c) 2006-2007, Ext JS, LLC.
43372  *
43373  * Originally Released Under LGPL - original licence link has changed is not relivant.
43374  *
43375  * Fork - LGPL
43376  * <script type="text/javascript">
43377  */
43378  
43379 /**
43380  * @class Roo.LayoutManager
43381  * @extends Roo.util.Observable
43382  * Base class for layout managers.
43383  */
43384 Roo.LayoutManager = function(container, config){
43385     Roo.LayoutManager.superclass.constructor.call(this);
43386     this.el = Roo.get(container);
43387     // ie scrollbar fix
43388     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43389         document.body.scroll = "no";
43390     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43391         this.el.position('relative');
43392     }
43393     this.id = this.el.id;
43394     this.el.addClass("x-layout-container");
43395     /** false to disable window resize monitoring @type Boolean */
43396     this.monitorWindowResize = true;
43397     this.regions = {};
43398     this.addEvents({
43399         /**
43400          * @event layout
43401          * Fires when a layout is performed. 
43402          * @param {Roo.LayoutManager} this
43403          */
43404         "layout" : true,
43405         /**
43406          * @event regionresized
43407          * Fires when the user resizes a region. 
43408          * @param {Roo.LayoutRegion} region The resized region
43409          * @param {Number} newSize The new size (width for east/west, height for north/south)
43410          */
43411         "regionresized" : true,
43412         /**
43413          * @event regioncollapsed
43414          * Fires when a region is collapsed. 
43415          * @param {Roo.LayoutRegion} region The collapsed region
43416          */
43417         "regioncollapsed" : true,
43418         /**
43419          * @event regionexpanded
43420          * Fires when a region is expanded.  
43421          * @param {Roo.LayoutRegion} region The expanded region
43422          */
43423         "regionexpanded" : true
43424     });
43425     this.updating = false;
43426     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43427 };
43428
43429 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43430     /**
43431      * Returns true if this layout is currently being updated
43432      * @return {Boolean}
43433      */
43434     isUpdating : function(){
43435         return this.updating; 
43436     },
43437     
43438     /**
43439      * Suspend the LayoutManager from doing auto-layouts while
43440      * making multiple add or remove calls
43441      */
43442     beginUpdate : function(){
43443         this.updating = true;    
43444     },
43445     
43446     /**
43447      * Restore auto-layouts and optionally disable the manager from performing a layout
43448      * @param {Boolean} noLayout true to disable a layout update 
43449      */
43450     endUpdate : function(noLayout){
43451         this.updating = false;
43452         if(!noLayout){
43453             this.layout();
43454         }    
43455     },
43456     
43457     layout: function(){
43458         
43459     },
43460     
43461     onRegionResized : function(region, newSize){
43462         this.fireEvent("regionresized", region, newSize);
43463         this.layout();
43464     },
43465     
43466     onRegionCollapsed : function(region){
43467         this.fireEvent("regioncollapsed", region);
43468     },
43469     
43470     onRegionExpanded : function(region){
43471         this.fireEvent("regionexpanded", region);
43472     },
43473         
43474     /**
43475      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43476      * performs box-model adjustments.
43477      * @return {Object} The size as an object {width: (the width), height: (the height)}
43478      */
43479     getViewSize : function(){
43480         var size;
43481         if(this.el.dom != document.body){
43482             size = this.el.getSize();
43483         }else{
43484             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43485         }
43486         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43487         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43488         return size;
43489     },
43490     
43491     /**
43492      * Returns the Element this layout is bound to.
43493      * @return {Roo.Element}
43494      */
43495     getEl : function(){
43496         return this.el;
43497     },
43498     
43499     /**
43500      * Returns the specified region.
43501      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43502      * @return {Roo.LayoutRegion}
43503      */
43504     getRegion : function(target){
43505         return this.regions[target.toLowerCase()];
43506     },
43507     
43508     onWindowResize : function(){
43509         if(this.monitorWindowResize){
43510             this.layout();
43511         }
43512     }
43513 });/*
43514  * Based on:
43515  * Ext JS Library 1.1.1
43516  * Copyright(c) 2006-2007, Ext JS, LLC.
43517  *
43518  * Originally Released Under LGPL - original licence link has changed is not relivant.
43519  *
43520  * Fork - LGPL
43521  * <script type="text/javascript">
43522  */
43523 /**
43524  * @class Roo.BorderLayout
43525  * @extends Roo.LayoutManager
43526  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43527  * please see: <br><br>
43528  * <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>
43529  * <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>
43530  * Example:
43531  <pre><code>
43532  var layout = new Roo.BorderLayout(document.body, {
43533     north: {
43534         initialSize: 25,
43535         titlebar: false
43536     },
43537     west: {
43538         split:true,
43539         initialSize: 200,
43540         minSize: 175,
43541         maxSize: 400,
43542         titlebar: true,
43543         collapsible: true
43544     },
43545     east: {
43546         split:true,
43547         initialSize: 202,
43548         minSize: 175,
43549         maxSize: 400,
43550         titlebar: true,
43551         collapsible: true
43552     },
43553     south: {
43554         split:true,
43555         initialSize: 100,
43556         minSize: 100,
43557         maxSize: 200,
43558         titlebar: true,
43559         collapsible: true
43560     },
43561     center: {
43562         titlebar: true,
43563         autoScroll:true,
43564         resizeTabs: true,
43565         minTabWidth: 50,
43566         preferredTabWidth: 150
43567     }
43568 });
43569
43570 // shorthand
43571 var CP = Roo.ContentPanel;
43572
43573 layout.beginUpdate();
43574 layout.add("north", new CP("north", "North"));
43575 layout.add("south", new CP("south", {title: "South", closable: true}));
43576 layout.add("west", new CP("west", {title: "West"}));
43577 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43578 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43579 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43580 layout.getRegion("center").showPanel("center1");
43581 layout.endUpdate();
43582 </code></pre>
43583
43584 <b>The container the layout is rendered into can be either the body element or any other element.
43585 If it is not the body element, the container needs to either be an absolute positioned element,
43586 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43587 the container size if it is not the body element.</b>
43588
43589 * @constructor
43590 * Create a new BorderLayout
43591 * @param {String/HTMLElement/Element} container The container this layout is bound to
43592 * @param {Object} config Configuration options
43593  */
43594 Roo.BorderLayout = function(container, config){
43595     config = config || {};
43596     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43597     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43598     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43599         var target = this.factory.validRegions[i];
43600         if(config[target]){
43601             this.addRegion(target, config[target]);
43602         }
43603     }
43604 };
43605
43606 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43607     /**
43608      * Creates and adds a new region if it doesn't already exist.
43609      * @param {String} target The target region key (north, south, east, west or center).
43610      * @param {Object} config The regions config object
43611      * @return {BorderLayoutRegion} The new region
43612      */
43613     addRegion : function(target, config){
43614         if(!this.regions[target]){
43615             var r = this.factory.create(target, this, config);
43616             this.bindRegion(target, r);
43617         }
43618         return this.regions[target];
43619     },
43620
43621     // private (kinda)
43622     bindRegion : function(name, r){
43623         this.regions[name] = r;
43624         r.on("visibilitychange", this.layout, this);
43625         r.on("paneladded", this.layout, this);
43626         r.on("panelremoved", this.layout, this);
43627         r.on("invalidated", this.layout, this);
43628         r.on("resized", this.onRegionResized, this);
43629         r.on("collapsed", this.onRegionCollapsed, this);
43630         r.on("expanded", this.onRegionExpanded, this);
43631     },
43632
43633     /**
43634      * Performs a layout update.
43635      */
43636     layout : function(){
43637         if(this.updating) return;
43638         var size = this.getViewSize();
43639         var w = size.width;
43640         var h = size.height;
43641         var centerW = w;
43642         var centerH = h;
43643         var centerY = 0;
43644         var centerX = 0;
43645         //var x = 0, y = 0;
43646
43647         var rs = this.regions;
43648         var north = rs["north"];
43649         var south = rs["south"]; 
43650         var west = rs["west"];
43651         var east = rs["east"];
43652         var center = rs["center"];
43653         //if(this.hideOnLayout){ // not supported anymore
43654             //c.el.setStyle("display", "none");
43655         //}
43656         if(north && north.isVisible()){
43657             var b = north.getBox();
43658             var m = north.getMargins();
43659             b.width = w - (m.left+m.right);
43660             b.x = m.left;
43661             b.y = m.top;
43662             centerY = b.height + b.y + m.bottom;
43663             centerH -= centerY;
43664             north.updateBox(this.safeBox(b));
43665         }
43666         if(south && south.isVisible()){
43667             var b = south.getBox();
43668             var m = south.getMargins();
43669             b.width = w - (m.left+m.right);
43670             b.x = m.left;
43671             var totalHeight = (b.height + m.top + m.bottom);
43672             b.y = h - totalHeight + m.top;
43673             centerH -= totalHeight;
43674             south.updateBox(this.safeBox(b));
43675         }
43676         if(west && west.isVisible()){
43677             var b = west.getBox();
43678             var m = west.getMargins();
43679             b.height = centerH - (m.top+m.bottom);
43680             b.x = m.left;
43681             b.y = centerY + m.top;
43682             var totalWidth = (b.width + m.left + m.right);
43683             centerX += totalWidth;
43684             centerW -= totalWidth;
43685             west.updateBox(this.safeBox(b));
43686         }
43687         if(east && east.isVisible()){
43688             var b = east.getBox();
43689             var m = east.getMargins();
43690             b.height = centerH - (m.top+m.bottom);
43691             var totalWidth = (b.width + m.left + m.right);
43692             b.x = w - totalWidth + m.left;
43693             b.y = centerY + m.top;
43694             centerW -= totalWidth;
43695             east.updateBox(this.safeBox(b));
43696         }
43697         if(center){
43698             var m = center.getMargins();
43699             var centerBox = {
43700                 x: centerX + m.left,
43701                 y: centerY + m.top,
43702                 width: centerW - (m.left+m.right),
43703                 height: centerH - (m.top+m.bottom)
43704             };
43705             //if(this.hideOnLayout){
43706                 //center.el.setStyle("display", "block");
43707             //}
43708             center.updateBox(this.safeBox(centerBox));
43709         }
43710         this.el.repaint();
43711         this.fireEvent("layout", this);
43712     },
43713
43714     // private
43715     safeBox : function(box){
43716         box.width = Math.max(0, box.width);
43717         box.height = Math.max(0, box.height);
43718         return box;
43719     },
43720
43721     /**
43722      * Adds a ContentPanel (or subclass) to this layout.
43723      * @param {String} target The target region key (north, south, east, west or center).
43724      * @param {Roo.ContentPanel} panel The panel to add
43725      * @return {Roo.ContentPanel} The added panel
43726      */
43727     add : function(target, panel){
43728          
43729         target = target.toLowerCase();
43730         return this.regions[target].add(panel);
43731     },
43732
43733     /**
43734      * Remove a ContentPanel (or subclass) to this layout.
43735      * @param {String} target The target region key (north, south, east, west or center).
43736      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43737      * @return {Roo.ContentPanel} The removed panel
43738      */
43739     remove : function(target, panel){
43740         target = target.toLowerCase();
43741         return this.regions[target].remove(panel);
43742     },
43743
43744     /**
43745      * Searches all regions for a panel with the specified id
43746      * @param {String} panelId
43747      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43748      */
43749     findPanel : function(panelId){
43750         var rs = this.regions;
43751         for(var target in rs){
43752             if(typeof rs[target] != "function"){
43753                 var p = rs[target].getPanel(panelId);
43754                 if(p){
43755                     return p;
43756                 }
43757             }
43758         }
43759         return null;
43760     },
43761
43762     /**
43763      * Searches all regions for a panel with the specified id and activates (shows) it.
43764      * @param {String/ContentPanel} panelId The panels id or the panel itself
43765      * @return {Roo.ContentPanel} The shown panel or null
43766      */
43767     showPanel : function(panelId) {
43768       var rs = this.regions;
43769       for(var target in rs){
43770          var r = rs[target];
43771          if(typeof r != "function"){
43772             if(r.hasPanel(panelId)){
43773                return r.showPanel(panelId);
43774             }
43775          }
43776       }
43777       return null;
43778    },
43779
43780    /**
43781      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43782      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43783      */
43784     restoreState : function(provider){
43785         if(!provider){
43786             provider = Roo.state.Manager;
43787         }
43788         var sm = new Roo.LayoutStateManager();
43789         sm.init(this, provider);
43790     },
43791
43792     /**
43793      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43794      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43795      * a valid ContentPanel config object.  Example:
43796      * <pre><code>
43797 // Create the main layout
43798 var layout = new Roo.BorderLayout('main-ct', {
43799     west: {
43800         split:true,
43801         minSize: 175,
43802         titlebar: true
43803     },
43804     center: {
43805         title:'Components'
43806     }
43807 }, 'main-ct');
43808
43809 // Create and add multiple ContentPanels at once via configs
43810 layout.batchAdd({
43811    west: {
43812        id: 'source-files',
43813        autoCreate:true,
43814        title:'Ext Source Files',
43815        autoScroll:true,
43816        fitToFrame:true
43817    },
43818    center : {
43819        el: cview,
43820        autoScroll:true,
43821        fitToFrame:true,
43822        toolbar: tb,
43823        resizeEl:'cbody'
43824    }
43825 });
43826 </code></pre>
43827      * @param {Object} regions An object containing ContentPanel configs by region name
43828      */
43829     batchAdd : function(regions){
43830         this.beginUpdate();
43831         for(var rname in regions){
43832             var lr = this.regions[rname];
43833             if(lr){
43834                 this.addTypedPanels(lr, regions[rname]);
43835             }
43836         }
43837         this.endUpdate();
43838     },
43839
43840     // private
43841     addTypedPanels : function(lr, ps){
43842         if(typeof ps == 'string'){
43843             lr.add(new Roo.ContentPanel(ps));
43844         }
43845         else if(ps instanceof Array){
43846             for(var i =0, len = ps.length; i < len; i++){
43847                 this.addTypedPanels(lr, ps[i]);
43848             }
43849         }
43850         else if(!ps.events){ // raw config?
43851             var el = ps.el;
43852             delete ps.el; // prevent conflict
43853             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43854         }
43855         else {  // panel object assumed!
43856             lr.add(ps);
43857         }
43858     },
43859     /**
43860      * Adds a xtype elements to the layout.
43861      * <pre><code>
43862
43863 layout.addxtype({
43864        xtype : 'ContentPanel',
43865        region: 'west',
43866        items: [ .... ]
43867    }
43868 );
43869
43870 layout.addxtype({
43871         xtype : 'NestedLayoutPanel',
43872         region: 'west',
43873         layout: {
43874            center: { },
43875            west: { }   
43876         },
43877         items : [ ... list of content panels or nested layout panels.. ]
43878    }
43879 );
43880 </code></pre>
43881      * @param {Object} cfg Xtype definition of item to add.
43882      */
43883     addxtype : function(cfg)
43884     {
43885         // basically accepts a pannel...
43886         // can accept a layout region..!?!?
43887        // console.log('BorderLayout add ' + cfg.xtype)
43888         
43889         if (!cfg.xtype.match(/Panel$/)) {
43890             return false;
43891         }
43892         var ret = false;
43893         var region = cfg.region;
43894         delete cfg.region;
43895         
43896           
43897         var xitems = [];
43898         if (cfg.items) {
43899             xitems = cfg.items;
43900             delete cfg.items;
43901         }
43902         
43903         
43904         switch(cfg.xtype) 
43905         {
43906             case 'ContentPanel':  // ContentPanel (el, cfg)
43907             case 'ScrollPanel':  // ContentPanel (el, cfg)
43908                 if(cfg.autoCreate) {
43909                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43910                 } else {
43911                     var el = this.el.createChild();
43912                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43913                 }
43914                 
43915                 this.add(region, ret);
43916                 break;
43917             
43918             
43919             case 'TreePanel': // our new panel!
43920                 cfg.el = this.el.createChild();
43921                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43922                 this.add(region, ret);
43923                 break;
43924             
43925             case 'NestedLayoutPanel': 
43926                 // create a new Layout (which is  a Border Layout...
43927                 var el = this.el.createChild();
43928                 var clayout = cfg.layout;
43929                 delete cfg.layout;
43930                 clayout.items   = clayout.items  || [];
43931                 // replace this exitems with the clayout ones..
43932                 xitems = clayout.items;
43933                  
43934                 
43935                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43936                     cfg.background = false;
43937                 }
43938                 var layout = new Roo.BorderLayout(el, clayout);
43939                 
43940                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43941                 //console.log('adding nested layout panel '  + cfg.toSource());
43942                 this.add(region, ret);
43943                 
43944                 break;
43945                 
43946             case 'GridPanel': 
43947             
43948                 // needs grid and region
43949                 
43950                 //var el = this.getRegion(region).el.createChild();
43951                 var el = this.el.createChild();
43952                 // create the grid first...
43953                 
43954                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43955                 delete cfg.grid;
43956                 if (region == 'center' && this.active ) {
43957                     cfg.background = false;
43958                 }
43959                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43960                 
43961                 this.add(region, ret);
43962                 if (cfg.background) {
43963                     ret.on('activate', function(gp) {
43964                         if (!gp.grid.rendered) {
43965                             gp.grid.render();
43966                         }
43967                     });
43968                 } else {
43969                     grid.render();
43970                 }
43971                 break;
43972            
43973                
43974                 
43975                 
43976             default: 
43977                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43978                 return null;
43979              // GridPanel (grid, cfg)
43980             
43981         }
43982         this.beginUpdate();
43983         // add children..
43984         Roo.each(xitems, function(i)  {
43985             ret.addxtype(i);
43986         });
43987         this.endUpdate();
43988         return ret;
43989         
43990     }
43991 });
43992
43993 /**
43994  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43995  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43996  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43997  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43998  * <pre><code>
43999 // shorthand
44000 var CP = Roo.ContentPanel;
44001
44002 var layout = Roo.BorderLayout.create({
44003     north: {
44004         initialSize: 25,
44005         titlebar: false,
44006         panels: [new CP("north", "North")]
44007     },
44008     west: {
44009         split:true,
44010         initialSize: 200,
44011         minSize: 175,
44012         maxSize: 400,
44013         titlebar: true,
44014         collapsible: true,
44015         panels: [new CP("west", {title: "West"})]
44016     },
44017     east: {
44018         split:true,
44019         initialSize: 202,
44020         minSize: 175,
44021         maxSize: 400,
44022         titlebar: true,
44023         collapsible: true,
44024         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44025     },
44026     south: {
44027         split:true,
44028         initialSize: 100,
44029         minSize: 100,
44030         maxSize: 200,
44031         titlebar: true,
44032         collapsible: true,
44033         panels: [new CP("south", {title: "South", closable: true})]
44034     },
44035     center: {
44036         titlebar: true,
44037         autoScroll:true,
44038         resizeTabs: true,
44039         minTabWidth: 50,
44040         preferredTabWidth: 150,
44041         panels: [
44042             new CP("center1", {title: "Close Me", closable: true}),
44043             new CP("center2", {title: "Center Panel", closable: false})
44044         ]
44045     }
44046 }, document.body);
44047
44048 layout.getRegion("center").showPanel("center1");
44049 </code></pre>
44050  * @param config
44051  * @param targetEl
44052  */
44053 Roo.BorderLayout.create = function(config, targetEl){
44054     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44055     layout.beginUpdate();
44056     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44057     for(var j = 0, jlen = regions.length; j < jlen; j++){
44058         var lr = regions[j];
44059         if(layout.regions[lr] && config[lr].panels){
44060             var r = layout.regions[lr];
44061             var ps = config[lr].panels;
44062             layout.addTypedPanels(r, ps);
44063         }
44064     }
44065     layout.endUpdate();
44066     return layout;
44067 };
44068
44069 // private
44070 Roo.BorderLayout.RegionFactory = {
44071     // private
44072     validRegions : ["north","south","east","west","center"],
44073
44074     // private
44075     create : function(target, mgr, config){
44076         target = target.toLowerCase();
44077         if(config.lightweight || config.basic){
44078             return new Roo.BasicLayoutRegion(mgr, config, target);
44079         }
44080         switch(target){
44081             case "north":
44082                 return new Roo.NorthLayoutRegion(mgr, config);
44083             case "south":
44084                 return new Roo.SouthLayoutRegion(mgr, config);
44085             case "east":
44086                 return new Roo.EastLayoutRegion(mgr, config);
44087             case "west":
44088                 return new Roo.WestLayoutRegion(mgr, config);
44089             case "center":
44090                 return new Roo.CenterLayoutRegion(mgr, config);
44091         }
44092         throw 'Layout region "'+target+'" not supported.';
44093     }
44094 };/*
44095  * Based on:
44096  * Ext JS Library 1.1.1
44097  * Copyright(c) 2006-2007, Ext JS, LLC.
44098  *
44099  * Originally Released Under LGPL - original licence link has changed is not relivant.
44100  *
44101  * Fork - LGPL
44102  * <script type="text/javascript">
44103  */
44104  
44105 /**
44106  * @class Roo.BasicLayoutRegion
44107  * @extends Roo.util.Observable
44108  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44109  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44110  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44111  */
44112 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44113     this.mgr = mgr;
44114     this.position  = pos;
44115     this.events = {
44116         /**
44117          * @scope Roo.BasicLayoutRegion
44118          */
44119         
44120         /**
44121          * @event beforeremove
44122          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44123          * @param {Roo.LayoutRegion} this
44124          * @param {Roo.ContentPanel} panel The panel
44125          * @param {Object} e The cancel event object
44126          */
44127         "beforeremove" : true,
44128         /**
44129          * @event invalidated
44130          * Fires when the layout for this region is changed.
44131          * @param {Roo.LayoutRegion} this
44132          */
44133         "invalidated" : true,
44134         /**
44135          * @event visibilitychange
44136          * Fires when this region is shown or hidden 
44137          * @param {Roo.LayoutRegion} this
44138          * @param {Boolean} visibility true or false
44139          */
44140         "visibilitychange" : true,
44141         /**
44142          * @event paneladded
44143          * Fires when a panel is added. 
44144          * @param {Roo.LayoutRegion} this
44145          * @param {Roo.ContentPanel} panel The panel
44146          */
44147         "paneladded" : true,
44148         /**
44149          * @event panelremoved
44150          * Fires when a panel is removed. 
44151          * @param {Roo.LayoutRegion} this
44152          * @param {Roo.ContentPanel} panel The panel
44153          */
44154         "panelremoved" : true,
44155         /**
44156          * @event collapsed
44157          * Fires when this region is collapsed.
44158          * @param {Roo.LayoutRegion} this
44159          */
44160         "collapsed" : true,
44161         /**
44162          * @event expanded
44163          * Fires when this region is expanded.
44164          * @param {Roo.LayoutRegion} this
44165          */
44166         "expanded" : true,
44167         /**
44168          * @event slideshow
44169          * Fires when this region is slid into view.
44170          * @param {Roo.LayoutRegion} this
44171          */
44172         "slideshow" : true,
44173         /**
44174          * @event slidehide
44175          * Fires when this region slides out of view. 
44176          * @param {Roo.LayoutRegion} this
44177          */
44178         "slidehide" : true,
44179         /**
44180          * @event panelactivated
44181          * Fires when a panel is activated. 
44182          * @param {Roo.LayoutRegion} this
44183          * @param {Roo.ContentPanel} panel The activated panel
44184          */
44185         "panelactivated" : true,
44186         /**
44187          * @event resized
44188          * Fires when the user resizes this region. 
44189          * @param {Roo.LayoutRegion} this
44190          * @param {Number} newSize The new size (width for east/west, height for north/south)
44191          */
44192         "resized" : true
44193     };
44194     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44195     this.panels = new Roo.util.MixedCollection();
44196     this.panels.getKey = this.getPanelId.createDelegate(this);
44197     this.box = null;
44198     this.activePanel = null;
44199     // ensure listeners are added...
44200     
44201     if (config.listeners || config.events) {
44202         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44203             listeners : config.listeners || {},
44204             events : config.events || {}
44205         });
44206     }
44207     
44208     if(skipConfig !== true){
44209         this.applyConfig(config);
44210     }
44211 };
44212
44213 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
44214     getPanelId : function(p){
44215         return p.getId();
44216     },
44217     
44218     applyConfig : function(config){
44219         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44220         this.config = config;
44221         
44222     },
44223     
44224     /**
44225      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
44226      * the width, for horizontal (north, south) the height.
44227      * @param {Number} newSize The new width or height
44228      */
44229     resizeTo : function(newSize){
44230         var el = this.el ? this.el :
44231                  (this.activePanel ? this.activePanel.getEl() : null);
44232         if(el){
44233             switch(this.position){
44234                 case "east":
44235                 case "west":
44236                     el.setWidth(newSize);
44237                     this.fireEvent("resized", this, newSize);
44238                 break;
44239                 case "north":
44240                 case "south":
44241                     el.setHeight(newSize);
44242                     this.fireEvent("resized", this, newSize);
44243                 break;                
44244             }
44245         }
44246     },
44247     
44248     getBox : function(){
44249         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
44250     },
44251     
44252     getMargins : function(){
44253         return this.margins;
44254     },
44255     
44256     updateBox : function(box){
44257         this.box = box;
44258         var el = this.activePanel.getEl();
44259         el.dom.style.left = box.x + "px";
44260         el.dom.style.top = box.y + "px";
44261         this.activePanel.setSize(box.width, box.height);
44262     },
44263     
44264     /**
44265      * Returns the container element for this region.
44266      * @return {Roo.Element}
44267      */
44268     getEl : function(){
44269         return this.activePanel;
44270     },
44271     
44272     /**
44273      * Returns true if this region is currently visible.
44274      * @return {Boolean}
44275      */
44276     isVisible : function(){
44277         return this.activePanel ? true : false;
44278     },
44279     
44280     setActivePanel : function(panel){
44281         panel = this.getPanel(panel);
44282         if(this.activePanel && this.activePanel != panel){
44283             this.activePanel.setActiveState(false);
44284             this.activePanel.getEl().setLeftTop(-10000,-10000);
44285         }
44286         this.activePanel = panel;
44287         panel.setActiveState(true);
44288         if(this.box){
44289             panel.setSize(this.box.width, this.box.height);
44290         }
44291         this.fireEvent("panelactivated", this, panel);
44292         this.fireEvent("invalidated");
44293     },
44294     
44295     /**
44296      * Show the specified panel.
44297      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
44298      * @return {Roo.ContentPanel} The shown panel or null
44299      */
44300     showPanel : function(panel){
44301         if(panel = this.getPanel(panel)){
44302             this.setActivePanel(panel);
44303         }
44304         return panel;
44305     },
44306     
44307     /**
44308      * Get the active panel for this region.
44309      * @return {Roo.ContentPanel} The active panel or null
44310      */
44311     getActivePanel : function(){
44312         return this.activePanel;
44313     },
44314     
44315     /**
44316      * Add the passed ContentPanel(s)
44317      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44318      * @return {Roo.ContentPanel} The panel added (if only one was added)
44319      */
44320     add : function(panel){
44321         if(arguments.length > 1){
44322             for(var i = 0, len = arguments.length; i < len; i++) {
44323                 this.add(arguments[i]);
44324             }
44325             return null;
44326         }
44327         if(this.hasPanel(panel)){
44328             this.showPanel(panel);
44329             return panel;
44330         }
44331         var el = panel.getEl();
44332         if(el.dom.parentNode != this.mgr.el.dom){
44333             this.mgr.el.dom.appendChild(el.dom);
44334         }
44335         if(panel.setRegion){
44336             panel.setRegion(this);
44337         }
44338         this.panels.add(panel);
44339         el.setStyle("position", "absolute");
44340         if(!panel.background){
44341             this.setActivePanel(panel);
44342             if(this.config.initialSize && this.panels.getCount()==1){
44343                 this.resizeTo(this.config.initialSize);
44344             }
44345         }
44346         this.fireEvent("paneladded", this, panel);
44347         return panel;
44348     },
44349     
44350     /**
44351      * Returns true if the panel is in this region.
44352      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44353      * @return {Boolean}
44354      */
44355     hasPanel : function(panel){
44356         if(typeof panel == "object"){ // must be panel obj
44357             panel = panel.getId();
44358         }
44359         return this.getPanel(panel) ? true : false;
44360     },
44361     
44362     /**
44363      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44364      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44365      * @param {Boolean} preservePanel Overrides the config preservePanel option
44366      * @return {Roo.ContentPanel} The panel that was removed
44367      */
44368     remove : function(panel, preservePanel){
44369         panel = this.getPanel(panel);
44370         if(!panel){
44371             return null;
44372         }
44373         var e = {};
44374         this.fireEvent("beforeremove", this, panel, e);
44375         if(e.cancel === true){
44376             return null;
44377         }
44378         var panelId = panel.getId();
44379         this.panels.removeKey(panelId);
44380         return panel;
44381     },
44382     
44383     /**
44384      * Returns the panel specified or null if it's not in this region.
44385      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44386      * @return {Roo.ContentPanel}
44387      */
44388     getPanel : function(id){
44389         if(typeof id == "object"){ // must be panel obj
44390             return id;
44391         }
44392         return this.panels.get(id);
44393     },
44394     
44395     /**
44396      * Returns this regions position (north/south/east/west/center).
44397      * @return {String} 
44398      */
44399     getPosition: function(){
44400         return this.position;    
44401     }
44402 });/*
44403  * Based on:
44404  * Ext JS Library 1.1.1
44405  * Copyright(c) 2006-2007, Ext JS, LLC.
44406  *
44407  * Originally Released Under LGPL - original licence link has changed is not relivant.
44408  *
44409  * Fork - LGPL
44410  * <script type="text/javascript">
44411  */
44412  
44413 /**
44414  * @class Roo.LayoutRegion
44415  * @extends Roo.BasicLayoutRegion
44416  * This class represents a region in a layout manager.
44417  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
44418  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
44419  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
44420  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44421  * @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})
44422  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
44423  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
44424  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
44425  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
44426  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
44427  * @cfg {String}    title           The title for the region (overrides panel titles)
44428  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
44429  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44430  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
44431  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44432  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
44433  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44434  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
44435  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
44436  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
44437  * @cfg {Boolean}   showPin         True to show a pin button
44438  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
44439  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
44440  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
44441  * @cfg {Number}    width           For East/West panels
44442  * @cfg {Number}    height          For North/South panels
44443  * @cfg {Boolean}   split           To show the splitter
44444  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
44445  */
44446 Roo.LayoutRegion = function(mgr, config, pos){
44447     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44448     var dh = Roo.DomHelper;
44449     /** This region's container element 
44450     * @type Roo.Element */
44451     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44452     /** This region's title element 
44453     * @type Roo.Element */
44454
44455     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44456         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44457         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44458     ]}, true);
44459     this.titleEl.enableDisplayMode();
44460     /** This region's title text element 
44461     * @type HTMLElement */
44462     this.titleTextEl = this.titleEl.dom.firstChild;
44463     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44464     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44465     this.closeBtn.enableDisplayMode();
44466     this.closeBtn.on("click", this.closeClicked, this);
44467     this.closeBtn.hide();
44468
44469     this.createBody(config);
44470     this.visible = true;
44471     this.collapsed = false;
44472
44473     if(config.hideWhenEmpty){
44474         this.hide();
44475         this.on("paneladded", this.validateVisibility, this);
44476         this.on("panelremoved", this.validateVisibility, this);
44477     }
44478     this.applyConfig(config);
44479 };
44480
44481 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44482
44483     createBody : function(){
44484         /** This region's body element 
44485         * @type Roo.Element */
44486         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44487     },
44488
44489     applyConfig : function(c){
44490         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44491             var dh = Roo.DomHelper;
44492             if(c.titlebar !== false){
44493                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44494                 this.collapseBtn.on("click", this.collapse, this);
44495                 this.collapseBtn.enableDisplayMode();
44496
44497                 if(c.showPin === true || this.showPin){
44498                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44499                     this.stickBtn.enableDisplayMode();
44500                     this.stickBtn.on("click", this.expand, this);
44501                     this.stickBtn.hide();
44502                 }
44503             }
44504             /** This region's collapsed element
44505             * @type Roo.Element */
44506             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44507                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44508             ]}, true);
44509             if(c.floatable !== false){
44510                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44511                this.collapsedEl.on("click", this.collapseClick, this);
44512             }
44513
44514             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44515                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44516                    id: "message", unselectable: "on", style:{"float":"left"}});
44517                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44518              }
44519             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44520             this.expandBtn.on("click", this.expand, this);
44521         }
44522         if(this.collapseBtn){
44523             this.collapseBtn.setVisible(c.collapsible == true);
44524         }
44525         this.cmargins = c.cmargins || this.cmargins ||
44526                          (this.position == "west" || this.position == "east" ?
44527                              {top: 0, left: 2, right:2, bottom: 0} :
44528                              {top: 2, left: 0, right:0, bottom: 2});
44529         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44530         this.bottomTabs = c.tabPosition != "top";
44531         this.autoScroll = c.autoScroll || false;
44532         if(this.autoScroll){
44533             this.bodyEl.setStyle("overflow", "auto");
44534         }else{
44535             this.bodyEl.setStyle("overflow", "hidden");
44536         }
44537         //if(c.titlebar !== false){
44538             if((!c.titlebar && !c.title) || c.titlebar === false){
44539                 this.titleEl.hide();
44540             }else{
44541                 this.titleEl.show();
44542                 if(c.title){
44543                     this.titleTextEl.innerHTML = c.title;
44544                 }
44545             }
44546         //}
44547         this.duration = c.duration || .30;
44548         this.slideDuration = c.slideDuration || .45;
44549         this.config = c;
44550         if(c.collapsed){
44551             this.collapse(true);
44552         }
44553         if(c.hidden){
44554             this.hide();
44555         }
44556     },
44557     /**
44558      * Returns true if this region is currently visible.
44559      * @return {Boolean}
44560      */
44561     isVisible : function(){
44562         return this.visible;
44563     },
44564
44565     /**
44566      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44567      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44568      */
44569     setCollapsedTitle : function(title){
44570         title = title || "&#160;";
44571         if(this.collapsedTitleTextEl){
44572             this.collapsedTitleTextEl.innerHTML = title;
44573         }
44574     },
44575
44576     getBox : function(){
44577         var b;
44578         if(!this.collapsed){
44579             b = this.el.getBox(false, true);
44580         }else{
44581             b = this.collapsedEl.getBox(false, true);
44582         }
44583         return b;
44584     },
44585
44586     getMargins : function(){
44587         return this.collapsed ? this.cmargins : this.margins;
44588     },
44589
44590     highlight : function(){
44591         this.el.addClass("x-layout-panel-dragover");
44592     },
44593
44594     unhighlight : function(){
44595         this.el.removeClass("x-layout-panel-dragover");
44596     },
44597
44598     updateBox : function(box){
44599         this.box = box;
44600         if(!this.collapsed){
44601             this.el.dom.style.left = box.x + "px";
44602             this.el.dom.style.top = box.y + "px";
44603             this.updateBody(box.width, box.height);
44604         }else{
44605             this.collapsedEl.dom.style.left = box.x + "px";
44606             this.collapsedEl.dom.style.top = box.y + "px";
44607             this.collapsedEl.setSize(box.width, box.height);
44608         }
44609         if(this.tabs){
44610             this.tabs.autoSizeTabs();
44611         }
44612     },
44613
44614     updateBody : function(w, h){
44615         if(w !== null){
44616             this.el.setWidth(w);
44617             w -= this.el.getBorderWidth("rl");
44618             if(this.config.adjustments){
44619                 w += this.config.adjustments[0];
44620             }
44621         }
44622         if(h !== null){
44623             this.el.setHeight(h);
44624             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44625             h -= this.el.getBorderWidth("tb");
44626             if(this.config.adjustments){
44627                 h += this.config.adjustments[1];
44628             }
44629             this.bodyEl.setHeight(h);
44630             if(this.tabs){
44631                 h = this.tabs.syncHeight(h);
44632             }
44633         }
44634         if(this.panelSize){
44635             w = w !== null ? w : this.panelSize.width;
44636             h = h !== null ? h : this.panelSize.height;
44637         }
44638         if(this.activePanel){
44639             var el = this.activePanel.getEl();
44640             w = w !== null ? w : el.getWidth();
44641             h = h !== null ? h : el.getHeight();
44642             this.panelSize = {width: w, height: h};
44643             this.activePanel.setSize(w, h);
44644         }
44645         if(Roo.isIE && this.tabs){
44646             this.tabs.el.repaint();
44647         }
44648     },
44649
44650     /**
44651      * Returns the container element for this region.
44652      * @return {Roo.Element}
44653      */
44654     getEl : function(){
44655         return this.el;
44656     },
44657
44658     /**
44659      * Hides this region.
44660      */
44661     hide : function(){
44662         if(!this.collapsed){
44663             this.el.dom.style.left = "-2000px";
44664             this.el.hide();
44665         }else{
44666             this.collapsedEl.dom.style.left = "-2000px";
44667             this.collapsedEl.hide();
44668         }
44669         this.visible = false;
44670         this.fireEvent("visibilitychange", this, false);
44671     },
44672
44673     /**
44674      * Shows this region if it was previously hidden.
44675      */
44676     show : function(){
44677         if(!this.collapsed){
44678             this.el.show();
44679         }else{
44680             this.collapsedEl.show();
44681         }
44682         this.visible = true;
44683         this.fireEvent("visibilitychange", this, true);
44684     },
44685
44686     closeClicked : function(){
44687         if(this.activePanel){
44688             this.remove(this.activePanel);
44689         }
44690     },
44691
44692     collapseClick : function(e){
44693         if(this.isSlid){
44694            e.stopPropagation();
44695            this.slideIn();
44696         }else{
44697            e.stopPropagation();
44698            this.slideOut();
44699         }
44700     },
44701
44702     /**
44703      * Collapses this region.
44704      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44705      */
44706     collapse : function(skipAnim){
44707         if(this.collapsed) return;
44708         this.collapsed = true;
44709         if(this.split){
44710             this.split.el.hide();
44711         }
44712         if(this.config.animate && skipAnim !== true){
44713             this.fireEvent("invalidated", this);
44714             this.animateCollapse();
44715         }else{
44716             this.el.setLocation(-20000,-20000);
44717             this.el.hide();
44718             this.collapsedEl.show();
44719             this.fireEvent("collapsed", this);
44720             this.fireEvent("invalidated", this);
44721         }
44722     },
44723
44724     animateCollapse : function(){
44725         // overridden
44726     },
44727
44728     /**
44729      * Expands this region if it was previously collapsed.
44730      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44731      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44732      */
44733     expand : function(e, skipAnim){
44734         if(e) e.stopPropagation();
44735         if(!this.collapsed || this.el.hasActiveFx()) return;
44736         if(this.isSlid){
44737             this.afterSlideIn();
44738             skipAnim = true;
44739         }
44740         this.collapsed = false;
44741         if(this.config.animate && skipAnim !== true){
44742             this.animateExpand();
44743         }else{
44744             this.el.show();
44745             if(this.split){
44746                 this.split.el.show();
44747             }
44748             this.collapsedEl.setLocation(-2000,-2000);
44749             this.collapsedEl.hide();
44750             this.fireEvent("invalidated", this);
44751             this.fireEvent("expanded", this);
44752         }
44753     },
44754
44755     animateExpand : function(){
44756         // overridden
44757     },
44758
44759     initTabs : function()
44760     {
44761         this.bodyEl.setStyle("overflow", "hidden");
44762         var ts = new Roo.TabPanel(
44763                 this.bodyEl.dom,
44764                 {
44765                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
44766                     disableTooltips: this.config.disableTabTips,
44767                     toolbar : this.config.toolbar
44768                 }
44769         );
44770         if(this.config.hideTabs){
44771             ts.stripWrap.setDisplayed(false);
44772         }
44773         this.tabs = ts;
44774         ts.resizeTabs = this.config.resizeTabs === true;
44775         ts.minTabWidth = this.config.minTabWidth || 40;
44776         ts.maxTabWidth = this.config.maxTabWidth || 250;
44777         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44778         ts.monitorResize = false;
44779         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44780         ts.bodyEl.addClass('x-layout-tabs-body');
44781         this.panels.each(this.initPanelAsTab, this);
44782     },
44783
44784     initPanelAsTab : function(panel){
44785         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44786                     this.config.closeOnTab && panel.isClosable());
44787         if(panel.tabTip !== undefined){
44788             ti.setTooltip(panel.tabTip);
44789         }
44790         ti.on("activate", function(){
44791               this.setActivePanel(panel);
44792         }, this);
44793         if(this.config.closeOnTab){
44794             ti.on("beforeclose", function(t, e){
44795                 e.cancel = true;
44796                 this.remove(panel);
44797             }, this);
44798         }
44799         return ti;
44800     },
44801
44802     updatePanelTitle : function(panel, title){
44803         if(this.activePanel == panel){
44804             this.updateTitle(title);
44805         }
44806         if(this.tabs){
44807             var ti = this.tabs.getTab(panel.getEl().id);
44808             ti.setText(title);
44809             if(panel.tabTip !== undefined){
44810                 ti.setTooltip(panel.tabTip);
44811             }
44812         }
44813     },
44814
44815     updateTitle : function(title){
44816         if(this.titleTextEl && !this.config.title){
44817             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44818         }
44819     },
44820
44821     setActivePanel : function(panel){
44822         panel = this.getPanel(panel);
44823         if(this.activePanel && this.activePanel != panel){
44824             this.activePanel.setActiveState(false);
44825         }
44826         this.activePanel = panel;
44827         panel.setActiveState(true);
44828         if(this.panelSize){
44829             panel.setSize(this.panelSize.width, this.panelSize.height);
44830         }
44831         if(this.closeBtn){
44832             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44833         }
44834         this.updateTitle(panel.getTitle());
44835         if(this.tabs){
44836             this.fireEvent("invalidated", this);
44837         }
44838         this.fireEvent("panelactivated", this, panel);
44839     },
44840
44841     /**
44842      * Shows the specified panel.
44843      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44844      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44845      */
44846     showPanel : function(panel){
44847         if(panel = this.getPanel(panel)){
44848             if(this.tabs){
44849                 var tab = this.tabs.getTab(panel.getEl().id);
44850                 if(tab.isHidden()){
44851                     this.tabs.unhideTab(tab.id);
44852                 }
44853                 tab.activate();
44854             }else{
44855                 this.setActivePanel(panel);
44856             }
44857         }
44858         return panel;
44859     },
44860
44861     /**
44862      * Get the active panel for this region.
44863      * @return {Roo.ContentPanel} The active panel or null
44864      */
44865     getActivePanel : function(){
44866         return this.activePanel;
44867     },
44868
44869     validateVisibility : function(){
44870         if(this.panels.getCount() < 1){
44871             this.updateTitle("&#160;");
44872             this.closeBtn.hide();
44873             this.hide();
44874         }else{
44875             if(!this.isVisible()){
44876                 this.show();
44877             }
44878         }
44879     },
44880
44881     /**
44882      * Adds the passed ContentPanel(s) to this region.
44883      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44884      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44885      */
44886     add : function(panel){
44887         if(arguments.length > 1){
44888             for(var i = 0, len = arguments.length; i < len; i++) {
44889                 this.add(arguments[i]);
44890             }
44891             return null;
44892         }
44893         if(this.hasPanel(panel)){
44894             this.showPanel(panel);
44895             return panel;
44896         }
44897         panel.setRegion(this);
44898         this.panels.add(panel);
44899         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44900             this.bodyEl.dom.appendChild(panel.getEl().dom);
44901             if(panel.background !== true){
44902                 this.setActivePanel(panel);
44903             }
44904             this.fireEvent("paneladded", this, panel);
44905             return panel;
44906         }
44907         if(!this.tabs){
44908             this.initTabs();
44909         }else{
44910             this.initPanelAsTab(panel);
44911         }
44912         if(panel.background !== true){
44913             this.tabs.activate(panel.getEl().id);
44914         }
44915         this.fireEvent("paneladded", this, panel);
44916         return panel;
44917     },
44918
44919     /**
44920      * Hides the tab for the specified panel.
44921      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44922      */
44923     hidePanel : function(panel){
44924         if(this.tabs && (panel = this.getPanel(panel))){
44925             this.tabs.hideTab(panel.getEl().id);
44926         }
44927     },
44928
44929     /**
44930      * Unhides the tab for a previously hidden panel.
44931      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44932      */
44933     unhidePanel : function(panel){
44934         if(this.tabs && (panel = this.getPanel(panel))){
44935             this.tabs.unhideTab(panel.getEl().id);
44936         }
44937     },
44938
44939     clearPanels : function(){
44940         while(this.panels.getCount() > 0){
44941              this.remove(this.panels.first());
44942         }
44943     },
44944
44945     /**
44946      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44947      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44948      * @param {Boolean} preservePanel Overrides the config preservePanel option
44949      * @return {Roo.ContentPanel} The panel that was removed
44950      */
44951     remove : function(panel, preservePanel){
44952         panel = this.getPanel(panel);
44953         if(!panel){
44954             return null;
44955         }
44956         var e = {};
44957         this.fireEvent("beforeremove", this, panel, e);
44958         if(e.cancel === true){
44959             return null;
44960         }
44961         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44962         var panelId = panel.getId();
44963         this.panels.removeKey(panelId);
44964         if(preservePanel){
44965             document.body.appendChild(panel.getEl().dom);
44966         }
44967         if(this.tabs){
44968             this.tabs.removeTab(panel.getEl().id);
44969         }else if (!preservePanel){
44970             this.bodyEl.dom.removeChild(panel.getEl().dom);
44971         }
44972         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44973             var p = this.panels.first();
44974             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44975             tempEl.appendChild(p.getEl().dom);
44976             this.bodyEl.update("");
44977             this.bodyEl.dom.appendChild(p.getEl().dom);
44978             tempEl = null;
44979             this.updateTitle(p.getTitle());
44980             this.tabs = null;
44981             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44982             this.setActivePanel(p);
44983         }
44984         panel.setRegion(null);
44985         if(this.activePanel == panel){
44986             this.activePanel = null;
44987         }
44988         if(this.config.autoDestroy !== false && preservePanel !== true){
44989             try{panel.destroy();}catch(e){}
44990         }
44991         this.fireEvent("panelremoved", this, panel);
44992         return panel;
44993     },
44994
44995     /**
44996      * Returns the TabPanel component used by this region
44997      * @return {Roo.TabPanel}
44998      */
44999     getTabs : function(){
45000         return this.tabs;
45001     },
45002
45003     createTool : function(parentEl, className){
45004         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45005             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45006         btn.addClassOnOver("x-layout-tools-button-over");
45007         return btn;
45008     }
45009 });/*
45010  * Based on:
45011  * Ext JS Library 1.1.1
45012  * Copyright(c) 2006-2007, Ext JS, LLC.
45013  *
45014  * Originally Released Under LGPL - original licence link has changed is not relivant.
45015  *
45016  * Fork - LGPL
45017  * <script type="text/javascript">
45018  */
45019  
45020
45021
45022 /**
45023  * @class Roo.SplitLayoutRegion
45024  * @extends Roo.LayoutRegion
45025  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45026  */
45027 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45028     this.cursor = cursor;
45029     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45030 };
45031
45032 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45033     splitTip : "Drag to resize.",
45034     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45035     useSplitTips : false,
45036
45037     applyConfig : function(config){
45038         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45039         if(config.split){
45040             if(!this.split){
45041                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45042                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45043                 /** The SplitBar for this region 
45044                 * @type Roo.SplitBar */
45045                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45046                 this.split.on("moved", this.onSplitMove, this);
45047                 this.split.useShim = config.useShim === true;
45048                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45049                 if(this.useSplitTips){
45050                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45051                 }
45052                 if(config.collapsible){
45053                     this.split.el.on("dblclick", this.collapse,  this);
45054                 }
45055             }
45056             if(typeof config.minSize != "undefined"){
45057                 this.split.minSize = config.minSize;
45058             }
45059             if(typeof config.maxSize != "undefined"){
45060                 this.split.maxSize = config.maxSize;
45061             }
45062             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45063                 this.hideSplitter();
45064             }
45065         }
45066     },
45067
45068     getHMaxSize : function(){
45069          var cmax = this.config.maxSize || 10000;
45070          var center = this.mgr.getRegion("center");
45071          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45072     },
45073
45074     getVMaxSize : function(){
45075          var cmax = this.config.maxSize || 10000;
45076          var center = this.mgr.getRegion("center");
45077          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45078     },
45079
45080     onSplitMove : function(split, newSize){
45081         this.fireEvent("resized", this, newSize);
45082     },
45083     
45084     /** 
45085      * Returns the {@link Roo.SplitBar} for this region.
45086      * @return {Roo.SplitBar}
45087      */
45088     getSplitBar : function(){
45089         return this.split;
45090     },
45091     
45092     hide : function(){
45093         this.hideSplitter();
45094         Roo.SplitLayoutRegion.superclass.hide.call(this);
45095     },
45096
45097     hideSplitter : function(){
45098         if(this.split){
45099             this.split.el.setLocation(-2000,-2000);
45100             this.split.el.hide();
45101         }
45102     },
45103
45104     show : function(){
45105         if(this.split){
45106             this.split.el.show();
45107         }
45108         Roo.SplitLayoutRegion.superclass.show.call(this);
45109     },
45110     
45111     beforeSlide: function(){
45112         if(Roo.isGecko){// firefox overflow auto bug workaround
45113             this.bodyEl.clip();
45114             if(this.tabs) this.tabs.bodyEl.clip();
45115             if(this.activePanel){
45116                 this.activePanel.getEl().clip();
45117                 
45118                 if(this.activePanel.beforeSlide){
45119                     this.activePanel.beforeSlide();
45120                 }
45121             }
45122         }
45123     },
45124     
45125     afterSlide : function(){
45126         if(Roo.isGecko){// firefox overflow auto bug workaround
45127             this.bodyEl.unclip();
45128             if(this.tabs) this.tabs.bodyEl.unclip();
45129             if(this.activePanel){
45130                 this.activePanel.getEl().unclip();
45131                 if(this.activePanel.afterSlide){
45132                     this.activePanel.afterSlide();
45133                 }
45134             }
45135         }
45136     },
45137
45138     initAutoHide : function(){
45139         if(this.autoHide !== false){
45140             if(!this.autoHideHd){
45141                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45142                 this.autoHideHd = {
45143                     "mouseout": function(e){
45144                         if(!e.within(this.el, true)){
45145                             st.delay(500);
45146                         }
45147                     },
45148                     "mouseover" : function(e){
45149                         st.cancel();
45150                     },
45151                     scope : this
45152                 };
45153             }
45154             this.el.on(this.autoHideHd);
45155         }
45156     },
45157
45158     clearAutoHide : function(){
45159         if(this.autoHide !== false){
45160             this.el.un("mouseout", this.autoHideHd.mouseout);
45161             this.el.un("mouseover", this.autoHideHd.mouseover);
45162         }
45163     },
45164
45165     clearMonitor : function(){
45166         Roo.get(document).un("click", this.slideInIf, this);
45167     },
45168
45169     // these names are backwards but not changed for compat
45170     slideOut : function(){
45171         if(this.isSlid || this.el.hasActiveFx()){
45172             return;
45173         }
45174         this.isSlid = true;
45175         if(this.collapseBtn){
45176             this.collapseBtn.hide();
45177         }
45178         this.closeBtnState = this.closeBtn.getStyle('display');
45179         this.closeBtn.hide();
45180         if(this.stickBtn){
45181             this.stickBtn.show();
45182         }
45183         this.el.show();
45184         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45185         this.beforeSlide();
45186         this.el.setStyle("z-index", 10001);
45187         this.el.slideIn(this.getSlideAnchor(), {
45188             callback: function(){
45189                 this.afterSlide();
45190                 this.initAutoHide();
45191                 Roo.get(document).on("click", this.slideInIf, this);
45192                 this.fireEvent("slideshow", this);
45193             },
45194             scope: this,
45195             block: true
45196         });
45197     },
45198
45199     afterSlideIn : function(){
45200         this.clearAutoHide();
45201         this.isSlid = false;
45202         this.clearMonitor();
45203         this.el.setStyle("z-index", "");
45204         if(this.collapseBtn){
45205             this.collapseBtn.show();
45206         }
45207         this.closeBtn.setStyle('display', this.closeBtnState);
45208         if(this.stickBtn){
45209             this.stickBtn.hide();
45210         }
45211         this.fireEvent("slidehide", this);
45212     },
45213
45214     slideIn : function(cb){
45215         if(!this.isSlid || this.el.hasActiveFx()){
45216             Roo.callback(cb);
45217             return;
45218         }
45219         this.isSlid = false;
45220         this.beforeSlide();
45221         this.el.slideOut(this.getSlideAnchor(), {
45222             callback: function(){
45223                 this.el.setLeftTop(-10000, -10000);
45224                 this.afterSlide();
45225                 this.afterSlideIn();
45226                 Roo.callback(cb);
45227             },
45228             scope: this,
45229             block: true
45230         });
45231     },
45232     
45233     slideInIf : function(e){
45234         if(!e.within(this.el)){
45235             this.slideIn();
45236         }
45237     },
45238
45239     animateCollapse : function(){
45240         this.beforeSlide();
45241         this.el.setStyle("z-index", 20000);
45242         var anchor = this.getSlideAnchor();
45243         this.el.slideOut(anchor, {
45244             callback : function(){
45245                 this.el.setStyle("z-index", "");
45246                 this.collapsedEl.slideIn(anchor, {duration:.3});
45247                 this.afterSlide();
45248                 this.el.setLocation(-10000,-10000);
45249                 this.el.hide();
45250                 this.fireEvent("collapsed", this);
45251             },
45252             scope: this,
45253             block: true
45254         });
45255     },
45256
45257     animateExpand : function(){
45258         this.beforeSlide();
45259         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
45260         this.el.setStyle("z-index", 20000);
45261         this.collapsedEl.hide({
45262             duration:.1
45263         });
45264         this.el.slideIn(this.getSlideAnchor(), {
45265             callback : function(){
45266                 this.el.setStyle("z-index", "");
45267                 this.afterSlide();
45268                 if(this.split){
45269                     this.split.el.show();
45270                 }
45271                 this.fireEvent("invalidated", this);
45272                 this.fireEvent("expanded", this);
45273             },
45274             scope: this,
45275             block: true
45276         });
45277     },
45278
45279     anchors : {
45280         "west" : "left",
45281         "east" : "right",
45282         "north" : "top",
45283         "south" : "bottom"
45284     },
45285
45286     sanchors : {
45287         "west" : "l",
45288         "east" : "r",
45289         "north" : "t",
45290         "south" : "b"
45291     },
45292
45293     canchors : {
45294         "west" : "tl-tr",
45295         "east" : "tr-tl",
45296         "north" : "tl-bl",
45297         "south" : "bl-tl"
45298     },
45299
45300     getAnchor : function(){
45301         return this.anchors[this.position];
45302     },
45303
45304     getCollapseAnchor : function(){
45305         return this.canchors[this.position];
45306     },
45307
45308     getSlideAnchor : function(){
45309         return this.sanchors[this.position];
45310     },
45311
45312     getAlignAdj : function(){
45313         var cm = this.cmargins;
45314         switch(this.position){
45315             case "west":
45316                 return [0, 0];
45317             break;
45318             case "east":
45319                 return [0, 0];
45320             break;
45321             case "north":
45322                 return [0, 0];
45323             break;
45324             case "south":
45325                 return [0, 0];
45326             break;
45327         }
45328     },
45329
45330     getExpandAdj : function(){
45331         var c = this.collapsedEl, cm = this.cmargins;
45332         switch(this.position){
45333             case "west":
45334                 return [-(cm.right+c.getWidth()+cm.left), 0];
45335             break;
45336             case "east":
45337                 return [cm.right+c.getWidth()+cm.left, 0];
45338             break;
45339             case "north":
45340                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45341             break;
45342             case "south":
45343                 return [0, cm.top+cm.bottom+c.getHeight()];
45344             break;
45345         }
45346     }
45347 });/*
45348  * Based on:
45349  * Ext JS Library 1.1.1
45350  * Copyright(c) 2006-2007, Ext JS, LLC.
45351  *
45352  * Originally Released Under LGPL - original licence link has changed is not relivant.
45353  *
45354  * Fork - LGPL
45355  * <script type="text/javascript">
45356  */
45357 /*
45358  * These classes are private internal classes
45359  */
45360 Roo.CenterLayoutRegion = function(mgr, config){
45361     Roo.LayoutRegion.call(this, mgr, config, "center");
45362     this.visible = true;
45363     this.minWidth = config.minWidth || 20;
45364     this.minHeight = config.minHeight || 20;
45365 };
45366
45367 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45368     hide : function(){
45369         // center panel can't be hidden
45370     },
45371     
45372     show : function(){
45373         // center panel can't be hidden
45374     },
45375     
45376     getMinWidth: function(){
45377         return this.minWidth;
45378     },
45379     
45380     getMinHeight: function(){
45381         return this.minHeight;
45382     }
45383 });
45384
45385
45386 Roo.NorthLayoutRegion = function(mgr, config){
45387     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45388     if(this.split){
45389         this.split.placement = Roo.SplitBar.TOP;
45390         this.split.orientation = Roo.SplitBar.VERTICAL;
45391         this.split.el.addClass("x-layout-split-v");
45392     }
45393     var size = config.initialSize || config.height;
45394     if(typeof size != "undefined"){
45395         this.el.setHeight(size);
45396     }
45397 };
45398 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45399     orientation: Roo.SplitBar.VERTICAL,
45400     getBox : function(){
45401         if(this.collapsed){
45402             return this.collapsedEl.getBox();
45403         }
45404         var box = this.el.getBox();
45405         if(this.split){
45406             box.height += this.split.el.getHeight();
45407         }
45408         return box;
45409     },
45410     
45411     updateBox : function(box){
45412         if(this.split && !this.collapsed){
45413             box.height -= this.split.el.getHeight();
45414             this.split.el.setLeft(box.x);
45415             this.split.el.setTop(box.y+box.height);
45416             this.split.el.setWidth(box.width);
45417         }
45418         if(this.collapsed){
45419             this.updateBody(box.width, null);
45420         }
45421         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45422     }
45423 });
45424
45425 Roo.SouthLayoutRegion = function(mgr, config){
45426     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45427     if(this.split){
45428         this.split.placement = Roo.SplitBar.BOTTOM;
45429         this.split.orientation = Roo.SplitBar.VERTICAL;
45430         this.split.el.addClass("x-layout-split-v");
45431     }
45432     var size = config.initialSize || config.height;
45433     if(typeof size != "undefined"){
45434         this.el.setHeight(size);
45435     }
45436 };
45437 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45438     orientation: Roo.SplitBar.VERTICAL,
45439     getBox : function(){
45440         if(this.collapsed){
45441             return this.collapsedEl.getBox();
45442         }
45443         var box = this.el.getBox();
45444         if(this.split){
45445             var sh = this.split.el.getHeight();
45446             box.height += sh;
45447             box.y -= sh;
45448         }
45449         return box;
45450     },
45451     
45452     updateBox : function(box){
45453         if(this.split && !this.collapsed){
45454             var sh = this.split.el.getHeight();
45455             box.height -= sh;
45456             box.y += sh;
45457             this.split.el.setLeft(box.x);
45458             this.split.el.setTop(box.y-sh);
45459             this.split.el.setWidth(box.width);
45460         }
45461         if(this.collapsed){
45462             this.updateBody(box.width, null);
45463         }
45464         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45465     }
45466 });
45467
45468 Roo.EastLayoutRegion = function(mgr, config){
45469     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45470     if(this.split){
45471         this.split.placement = Roo.SplitBar.RIGHT;
45472         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45473         this.split.el.addClass("x-layout-split-h");
45474     }
45475     var size = config.initialSize || config.width;
45476     if(typeof size != "undefined"){
45477         this.el.setWidth(size);
45478     }
45479 };
45480 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45481     orientation: Roo.SplitBar.HORIZONTAL,
45482     getBox : function(){
45483         if(this.collapsed){
45484             return this.collapsedEl.getBox();
45485         }
45486         var box = this.el.getBox();
45487         if(this.split){
45488             var sw = this.split.el.getWidth();
45489             box.width += sw;
45490             box.x -= sw;
45491         }
45492         return box;
45493     },
45494
45495     updateBox : function(box){
45496         if(this.split && !this.collapsed){
45497             var sw = this.split.el.getWidth();
45498             box.width -= sw;
45499             this.split.el.setLeft(box.x);
45500             this.split.el.setTop(box.y);
45501             this.split.el.setHeight(box.height);
45502             box.x += sw;
45503         }
45504         if(this.collapsed){
45505             this.updateBody(null, box.height);
45506         }
45507         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45508     }
45509 });
45510
45511 Roo.WestLayoutRegion = function(mgr, config){
45512     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45513     if(this.split){
45514         this.split.placement = Roo.SplitBar.LEFT;
45515         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45516         this.split.el.addClass("x-layout-split-h");
45517     }
45518     var size = config.initialSize || config.width;
45519     if(typeof size != "undefined"){
45520         this.el.setWidth(size);
45521     }
45522 };
45523 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45524     orientation: Roo.SplitBar.HORIZONTAL,
45525     getBox : function(){
45526         if(this.collapsed){
45527             return this.collapsedEl.getBox();
45528         }
45529         var box = this.el.getBox();
45530         if(this.split){
45531             box.width += this.split.el.getWidth();
45532         }
45533         return box;
45534     },
45535     
45536     updateBox : function(box){
45537         if(this.split && !this.collapsed){
45538             var sw = this.split.el.getWidth();
45539             box.width -= sw;
45540             this.split.el.setLeft(box.x+box.width);
45541             this.split.el.setTop(box.y);
45542             this.split.el.setHeight(box.height);
45543         }
45544         if(this.collapsed){
45545             this.updateBody(null, box.height);
45546         }
45547         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45548     }
45549 });
45550 /*
45551  * Based on:
45552  * Ext JS Library 1.1.1
45553  * Copyright(c) 2006-2007, Ext JS, LLC.
45554  *
45555  * Originally Released Under LGPL - original licence link has changed is not relivant.
45556  *
45557  * Fork - LGPL
45558  * <script type="text/javascript">
45559  */
45560  
45561  
45562 /*
45563  * Private internal class for reading and applying state
45564  */
45565 Roo.LayoutStateManager = function(layout){
45566      // default empty state
45567      this.state = {
45568         north: {},
45569         south: {},
45570         east: {},
45571         west: {}       
45572     };
45573 };
45574
45575 Roo.LayoutStateManager.prototype = {
45576     init : function(layout, provider){
45577         this.provider = provider;
45578         var state = provider.get(layout.id+"-layout-state");
45579         if(state){
45580             var wasUpdating = layout.isUpdating();
45581             if(!wasUpdating){
45582                 layout.beginUpdate();
45583             }
45584             for(var key in state){
45585                 if(typeof state[key] != "function"){
45586                     var rstate = state[key];
45587                     var r = layout.getRegion(key);
45588                     if(r && rstate){
45589                         if(rstate.size){
45590                             r.resizeTo(rstate.size);
45591                         }
45592                         if(rstate.collapsed == true){
45593                             r.collapse(true);
45594                         }else{
45595                             r.expand(null, true);
45596                         }
45597                     }
45598                 }
45599             }
45600             if(!wasUpdating){
45601                 layout.endUpdate();
45602             }
45603             this.state = state; 
45604         }
45605         this.layout = layout;
45606         layout.on("regionresized", this.onRegionResized, this);
45607         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45608         layout.on("regionexpanded", this.onRegionExpanded, this);
45609     },
45610     
45611     storeState : function(){
45612         this.provider.set(this.layout.id+"-layout-state", this.state);
45613     },
45614     
45615     onRegionResized : function(region, newSize){
45616         this.state[region.getPosition()].size = newSize;
45617         this.storeState();
45618     },
45619     
45620     onRegionCollapsed : function(region){
45621         this.state[region.getPosition()].collapsed = true;
45622         this.storeState();
45623     },
45624     
45625     onRegionExpanded : function(region){
45626         this.state[region.getPosition()].collapsed = false;
45627         this.storeState();
45628     }
45629 };/*
45630  * Based on:
45631  * Ext JS Library 1.1.1
45632  * Copyright(c) 2006-2007, Ext JS, LLC.
45633  *
45634  * Originally Released Under LGPL - original licence link has changed is not relivant.
45635  *
45636  * Fork - LGPL
45637  * <script type="text/javascript">
45638  */
45639 /**
45640  * @class Roo.ContentPanel
45641  * @extends Roo.util.Observable
45642  * A basic ContentPanel element.
45643  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45644  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45645  * @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
45646  * @cfg {Boolean}   closable      True if the panel can be closed/removed
45647  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
45648  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45649  * @cfg {Toolbar}   toolbar       A toolbar for this panel
45650  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
45651  * @cfg {String} title          The title for this panel
45652  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45653  * @cfg {String} url            Calls {@link #setUrl} with this value
45654  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45655  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
45656  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
45657  * @cfg {String} content        Raw content to fill content panel with (uses setContent on construction.)
45658
45659  * @constructor
45660  * Create a new ContentPanel.
45661  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45662  * @param {String/Object} config A string to set only the title or a config object
45663  * @param {String} content (optional) Set the HTML content for this panel
45664  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45665  */
45666 Roo.ContentPanel = function(el, config, content){
45667     
45668      
45669     /*
45670     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45671         config = el;
45672         el = Roo.id();
45673     }
45674     if (config && config.parentLayout) { 
45675         el = config.parentLayout.el.createChild(); 
45676     }
45677     */
45678     if(el.autoCreate){ // xtype is available if this is called from factory
45679         config = el;
45680         el = Roo.id();
45681     }
45682     this.el = Roo.get(el);
45683     if(!this.el && config && config.autoCreate){
45684         if(typeof config.autoCreate == "object"){
45685             if(!config.autoCreate.id){
45686                 config.autoCreate.id = config.id||el;
45687             }
45688             this.el = Roo.DomHelper.append(document.body,
45689                         config.autoCreate, true);
45690         }else{
45691             this.el = Roo.DomHelper.append(document.body,
45692                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45693         }
45694     }
45695     this.closable = false;
45696     this.loaded = false;
45697     this.active = false;
45698     if(typeof config == "string"){
45699         this.title = config;
45700     }else{
45701         Roo.apply(this, config);
45702     }
45703     
45704     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45705         this.wrapEl = this.el.wrap();    
45706         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45707         
45708     }
45709     
45710     
45711     
45712     if(this.resizeEl){
45713         this.resizeEl = Roo.get(this.resizeEl, true);
45714     }else{
45715         this.resizeEl = this.el;
45716     }
45717     this.addEvents({
45718         /**
45719          * @event activate
45720          * Fires when this panel is activated. 
45721          * @param {Roo.ContentPanel} this
45722          */
45723         "activate" : true,
45724         /**
45725          * @event deactivate
45726          * Fires when this panel is activated. 
45727          * @param {Roo.ContentPanel} this
45728          */
45729         "deactivate" : true,
45730
45731         /**
45732          * @event resize
45733          * Fires when this panel is resized if fitToFrame is true.
45734          * @param {Roo.ContentPanel} this
45735          * @param {Number} width The width after any component adjustments
45736          * @param {Number} height The height after any component adjustments
45737          */
45738         "resize" : true
45739     });
45740     if(this.autoScroll){
45741         this.resizeEl.setStyle("overflow", "auto");
45742     } else {
45743         // fix randome scrolling
45744         this.el.on('scroll', function() {
45745             Roo.log('fix random scolling');
45746             this.scrollTo('top',0); 
45747         });
45748     }
45749     content = content || this.content;
45750     if(content){
45751         this.setContent(content);
45752     }
45753     if(config && config.url){
45754         this.setUrl(this.url, this.params, this.loadOnce);
45755     }
45756     
45757     
45758     
45759     Roo.ContentPanel.superclass.constructor.call(this);
45760 };
45761
45762 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45763     tabTip:'',
45764     setRegion : function(region){
45765         this.region = region;
45766         if(region){
45767            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45768         }else{
45769            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45770         } 
45771     },
45772     
45773     /**
45774      * Returns the toolbar for this Panel if one was configured. 
45775      * @return {Roo.Toolbar} 
45776      */
45777     getToolbar : function(){
45778         return this.toolbar;
45779     },
45780     
45781     setActiveState : function(active){
45782         this.active = active;
45783         if(!active){
45784             this.fireEvent("deactivate", this);
45785         }else{
45786             this.fireEvent("activate", this);
45787         }
45788     },
45789     /**
45790      * Updates this panel's element
45791      * @param {String} content The new content
45792      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45793     */
45794     setContent : function(content, loadScripts){
45795         this.el.update(content, loadScripts);
45796     },
45797
45798     ignoreResize : function(w, h){
45799         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45800             return true;
45801         }else{
45802             this.lastSize = {width: w, height: h};
45803             return false;
45804         }
45805     },
45806     /**
45807      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45808      * @return {Roo.UpdateManager} The UpdateManager
45809      */
45810     getUpdateManager : function(){
45811         return this.el.getUpdateManager();
45812     },
45813      /**
45814      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45815      * @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:
45816 <pre><code>
45817 panel.load({
45818     url: "your-url.php",
45819     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45820     callback: yourFunction,
45821     scope: yourObject, //(optional scope)
45822     discardUrl: false,
45823     nocache: false,
45824     text: "Loading...",
45825     timeout: 30,
45826     scripts: false
45827 });
45828 </code></pre>
45829      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45830      * 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.
45831      * @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}
45832      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45833      * @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.
45834      * @return {Roo.ContentPanel} this
45835      */
45836     load : function(){
45837         var um = this.el.getUpdateManager();
45838         um.update.apply(um, arguments);
45839         return this;
45840     },
45841
45842
45843     /**
45844      * 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.
45845      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45846      * @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)
45847      * @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)
45848      * @return {Roo.UpdateManager} The UpdateManager
45849      */
45850     setUrl : function(url, params, loadOnce){
45851         if(this.refreshDelegate){
45852             this.removeListener("activate", this.refreshDelegate);
45853         }
45854         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45855         this.on("activate", this.refreshDelegate);
45856         return this.el.getUpdateManager();
45857     },
45858     
45859     _handleRefresh : function(url, params, loadOnce){
45860         if(!loadOnce || !this.loaded){
45861             var updater = this.el.getUpdateManager();
45862             updater.update(url, params, this._setLoaded.createDelegate(this));
45863         }
45864     },
45865     
45866     _setLoaded : function(){
45867         this.loaded = true;
45868     }, 
45869     
45870     /**
45871      * Returns this panel's id
45872      * @return {String} 
45873      */
45874     getId : function(){
45875         return this.el.id;
45876     },
45877     
45878     /** 
45879      * Returns this panel's element - used by regiosn to add.
45880      * @return {Roo.Element} 
45881      */
45882     getEl : function(){
45883         return this.wrapEl || this.el;
45884     },
45885     
45886     adjustForComponents : function(width, height){
45887         if(this.resizeEl != this.el){
45888             width -= this.el.getFrameWidth('lr');
45889             height -= this.el.getFrameWidth('tb');
45890         }
45891         if(this.toolbar){
45892             var te = this.toolbar.getEl();
45893             height -= te.getHeight();
45894             te.setWidth(width);
45895         }
45896         if(this.adjustments){
45897             width += this.adjustments[0];
45898             height += this.adjustments[1];
45899         }
45900         return {"width": width, "height": height};
45901     },
45902     
45903     setSize : function(width, height){
45904         if(this.fitToFrame && !this.ignoreResize(width, height)){
45905             if(this.fitContainer && this.resizeEl != this.el){
45906                 this.el.setSize(width, height);
45907             }
45908             var size = this.adjustForComponents(width, height);
45909             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45910             this.fireEvent('resize', this, size.width, size.height);
45911         }
45912     },
45913     
45914     /**
45915      * Returns this panel's title
45916      * @return {String} 
45917      */
45918     getTitle : function(){
45919         return this.title;
45920     },
45921     
45922     /**
45923      * Set this panel's title
45924      * @param {String} title
45925      */
45926     setTitle : function(title){
45927         this.title = title;
45928         if(this.region){
45929             this.region.updatePanelTitle(this, title);
45930         }
45931     },
45932     
45933     /**
45934      * Returns true is this panel was configured to be closable
45935      * @return {Boolean} 
45936      */
45937     isClosable : function(){
45938         return this.closable;
45939     },
45940     
45941     beforeSlide : function(){
45942         this.el.clip();
45943         this.resizeEl.clip();
45944     },
45945     
45946     afterSlide : function(){
45947         this.el.unclip();
45948         this.resizeEl.unclip();
45949     },
45950     
45951     /**
45952      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45953      *   Will fail silently if the {@link #setUrl} method has not been called.
45954      *   This does not activate the panel, just updates its content.
45955      */
45956     refresh : function(){
45957         if(this.refreshDelegate){
45958            this.loaded = false;
45959            this.refreshDelegate();
45960         }
45961     },
45962     
45963     /**
45964      * Destroys this panel
45965      */
45966     destroy : function(){
45967         this.el.removeAllListeners();
45968         var tempEl = document.createElement("span");
45969         tempEl.appendChild(this.el.dom);
45970         tempEl.innerHTML = "";
45971         this.el.remove();
45972         this.el = null;
45973     },
45974     
45975     /**
45976      * form - if the content panel contains a form - this is a reference to it.
45977      * @type {Roo.form.Form}
45978      */
45979     form : false,
45980     /**
45981      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45982      *    This contains a reference to it.
45983      * @type {Roo.View}
45984      */
45985     view : false,
45986     
45987       /**
45988      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45989      * <pre><code>
45990
45991 layout.addxtype({
45992        xtype : 'Form',
45993        items: [ .... ]
45994    }
45995 );
45996
45997 </code></pre>
45998      * @param {Object} cfg Xtype definition of item to add.
45999      */
46000     
46001     addxtype : function(cfg) {
46002         // add form..
46003         if (cfg.xtype.match(/^Form$/)) {
46004             var el = this.el.createChild();
46005
46006             this.form = new  Roo.form.Form(cfg);
46007             
46008             
46009             if ( this.form.allItems.length) this.form.render(el.dom);
46010             return this.form;
46011         }
46012         // should only have one of theses..
46013         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46014             // views..
46015             cfg.el = this.el.appendChild(document.createElement("div"));
46016             // factory?
46017             var ret = new Roo[cfg.xtype](cfg);
46018             ret.render && ret.render(false, ''); // render blank..
46019             this.view = ret;
46020             return ret;
46021         }
46022         return false;
46023     }
46024 });
46025
46026 /**
46027  * @class Roo.GridPanel
46028  * @extends Roo.ContentPanel
46029  * @constructor
46030  * Create a new GridPanel.
46031  * @param {Roo.grid.Grid} grid The grid for this panel
46032  * @param {String/Object} config A string to set only the panel's title, or a config object
46033  */
46034 Roo.GridPanel = function(grid, config){
46035     
46036   
46037     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46038         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46039         
46040     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46041     
46042     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46043     
46044     if(this.toolbar){
46045         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46046     }
46047     // xtype created footer. - not sure if will work as we normally have to render first..
46048     if (this.footer && !this.footer.el && this.footer.xtype) {
46049         
46050         this.footer.container = this.grid.getView().getFooterPanel(true);
46051         this.footer.dataSource = this.grid.dataSource;
46052         this.footer = Roo.factory(this.footer, Roo);
46053         
46054     }
46055     
46056     grid.monitorWindowResize = false; // turn off autosizing
46057     grid.autoHeight = false;
46058     grid.autoWidth = false;
46059     this.grid = grid;
46060     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46061 };
46062
46063 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46064     getId : function(){
46065         return this.grid.id;
46066     },
46067     
46068     /**
46069      * Returns the grid for this panel
46070      * @return {Roo.grid.Grid} 
46071      */
46072     getGrid : function(){
46073         return this.grid;    
46074     },
46075     
46076     setSize : function(width, height){
46077         if(!this.ignoreResize(width, height)){
46078             var grid = this.grid;
46079             var size = this.adjustForComponents(width, height);
46080             grid.getGridEl().setSize(size.width, size.height);
46081             grid.autoSize();
46082         }
46083     },
46084     
46085     beforeSlide : function(){
46086         this.grid.getView().scroller.clip();
46087     },
46088     
46089     afterSlide : function(){
46090         this.grid.getView().scroller.unclip();
46091     },
46092     
46093     destroy : function(){
46094         this.grid.destroy();
46095         delete this.grid;
46096         Roo.GridPanel.superclass.destroy.call(this); 
46097     }
46098 });
46099
46100
46101 /**
46102  * @class Roo.NestedLayoutPanel
46103  * @extends Roo.ContentPanel
46104  * @constructor
46105  * Create a new NestedLayoutPanel.
46106  * 
46107  * 
46108  * @param {Roo.BorderLayout} layout The layout for this panel
46109  * @param {String/Object} config A string to set only the title or a config object
46110  */
46111 Roo.NestedLayoutPanel = function(layout, config)
46112 {
46113     // construct with only one argument..
46114     /* FIXME - implement nicer consturctors
46115     if (layout.layout) {
46116         config = layout;
46117         layout = config.layout;
46118         delete config.layout;
46119     }
46120     if (layout.xtype && !layout.getEl) {
46121         // then layout needs constructing..
46122         layout = Roo.factory(layout, Roo);
46123     }
46124     */
46125     
46126     
46127     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46128     
46129     layout.monitorWindowResize = false; // turn off autosizing
46130     this.layout = layout;
46131     this.layout.getEl().addClass("x-layout-nested-layout");
46132     
46133     
46134     
46135     
46136 };
46137
46138 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46139
46140     setSize : function(width, height){
46141         if(!this.ignoreResize(width, height)){
46142             var size = this.adjustForComponents(width, height);
46143             var el = this.layout.getEl();
46144             el.setSize(size.width, size.height);
46145             var touch = el.dom.offsetWidth;
46146             this.layout.layout();
46147             // ie requires a double layout on the first pass
46148             if(Roo.isIE && !this.initialized){
46149                 this.initialized = true;
46150                 this.layout.layout();
46151             }
46152         }
46153     },
46154     
46155     // activate all subpanels if not currently active..
46156     
46157     setActiveState : function(active){
46158         this.active = active;
46159         if(!active){
46160             this.fireEvent("deactivate", this);
46161             return;
46162         }
46163         
46164         this.fireEvent("activate", this);
46165         // not sure if this should happen before or after..
46166         if (!this.layout) {
46167             return; // should not happen..
46168         }
46169         var reg = false;
46170         for (var r in this.layout.regions) {
46171             reg = this.layout.getRegion(r);
46172             if (reg.getActivePanel()) {
46173                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46174                 reg.setActivePanel(reg.getActivePanel());
46175                 continue;
46176             }
46177             if (!reg.panels.length) {
46178                 continue;
46179             }
46180             reg.showPanel(reg.getPanel(0));
46181         }
46182         
46183         
46184         
46185         
46186     },
46187     
46188     /**
46189      * Returns the nested BorderLayout for this panel
46190      * @return {Roo.BorderLayout} 
46191      */
46192     getLayout : function(){
46193         return this.layout;
46194     },
46195     
46196      /**
46197      * Adds a xtype elements to the layout of the nested panel
46198      * <pre><code>
46199
46200 panel.addxtype({
46201        xtype : 'ContentPanel',
46202        region: 'west',
46203        items: [ .... ]
46204    }
46205 );
46206
46207 panel.addxtype({
46208         xtype : 'NestedLayoutPanel',
46209         region: 'west',
46210         layout: {
46211            center: { },
46212            west: { }   
46213         },
46214         items : [ ... list of content panels or nested layout panels.. ]
46215    }
46216 );
46217 </code></pre>
46218      * @param {Object} cfg Xtype definition of item to add.
46219      */
46220     addxtype : function(cfg) {
46221         return this.layout.addxtype(cfg);
46222     
46223     }
46224 });
46225
46226 Roo.ScrollPanel = function(el, config, content){
46227     config = config || {};
46228     config.fitToFrame = true;
46229     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
46230     
46231     this.el.dom.style.overflow = "hidden";
46232     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
46233     this.el.removeClass("x-layout-inactive-content");
46234     this.el.on("mousewheel", this.onWheel, this);
46235
46236     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
46237     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
46238     up.unselectable(); down.unselectable();
46239     up.on("click", this.scrollUp, this);
46240     down.on("click", this.scrollDown, this);
46241     up.addClassOnOver("x-scroller-btn-over");
46242     down.addClassOnOver("x-scroller-btn-over");
46243     up.addClassOnClick("x-scroller-btn-click");
46244     down.addClassOnClick("x-scroller-btn-click");
46245     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
46246
46247     this.resizeEl = this.el;
46248     this.el = wrap; this.up = up; this.down = down;
46249 };
46250
46251 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
46252     increment : 100,
46253     wheelIncrement : 5,
46254     scrollUp : function(){
46255         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
46256     },
46257
46258     scrollDown : function(){
46259         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
46260     },
46261
46262     afterScroll : function(){
46263         var el = this.resizeEl;
46264         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
46265         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46266         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
46267     },
46268
46269     setSize : function(){
46270         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
46271         this.afterScroll();
46272     },
46273
46274     onWheel : function(e){
46275         var d = e.getWheelDelta();
46276         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
46277         this.afterScroll();
46278         e.stopEvent();
46279     },
46280
46281     setContent : function(content, loadScripts){
46282         this.resizeEl.update(content, loadScripts);
46283     }
46284
46285 });
46286
46287
46288
46289
46290
46291
46292
46293
46294
46295 /**
46296  * @class Roo.TreePanel
46297  * @extends Roo.ContentPanel
46298  * @constructor
46299  * Create a new TreePanel. - defaults to fit/scoll contents.
46300  * @param {String/Object} config A string to set only the panel's title, or a config object
46301  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
46302  */
46303 Roo.TreePanel = function(config){
46304     var el = config.el;
46305     var tree = config.tree;
46306     delete config.tree; 
46307     delete config.el; // hopefull!
46308     
46309     // wrapper for IE7 strict & safari scroll issue
46310     
46311     var treeEl = el.createChild();
46312     config.resizeEl = treeEl;
46313     
46314     
46315     
46316     Roo.TreePanel.superclass.constructor.call(this, el, config);
46317  
46318  
46319     this.tree = new Roo.tree.TreePanel(treeEl , tree);
46320     //console.log(tree);
46321     this.on('activate', function()
46322     {
46323         if (this.tree.rendered) {
46324             return;
46325         }
46326         //console.log('render tree');
46327         this.tree.render();
46328     });
46329     
46330     this.on('resize',  function (cp, w, h) {
46331             this.tree.innerCt.setWidth(w);
46332             this.tree.innerCt.setHeight(h);
46333             this.tree.innerCt.setStyle('overflow-y', 'auto');
46334     });
46335
46336         
46337     
46338 };
46339
46340 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
46341     fitToFrame : true,
46342     autoScroll : true
46343 });
46344
46345
46346
46347
46348
46349
46350
46351
46352
46353
46354
46355 /*
46356  * Based on:
46357  * Ext JS Library 1.1.1
46358  * Copyright(c) 2006-2007, Ext JS, LLC.
46359  *
46360  * Originally Released Under LGPL - original licence link has changed is not relivant.
46361  *
46362  * Fork - LGPL
46363  * <script type="text/javascript">
46364  */
46365  
46366
46367 /**
46368  * @class Roo.ReaderLayout
46369  * @extends Roo.BorderLayout
46370  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46371  * center region containing two nested regions (a top one for a list view and one for item preview below),
46372  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46373  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46374  * expedites the setup of the overall layout and regions for this common application style.
46375  * Example:
46376  <pre><code>
46377 var reader = new Roo.ReaderLayout();
46378 var CP = Roo.ContentPanel;  // shortcut for adding
46379
46380 reader.beginUpdate();
46381 reader.add("north", new CP("north", "North"));
46382 reader.add("west", new CP("west", {title: "West"}));
46383 reader.add("east", new CP("east", {title: "East"}));
46384
46385 reader.regions.listView.add(new CP("listView", "List"));
46386 reader.regions.preview.add(new CP("preview", "Preview"));
46387 reader.endUpdate();
46388 </code></pre>
46389 * @constructor
46390 * Create a new ReaderLayout
46391 * @param {Object} config Configuration options
46392 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46393 * document.body if omitted)
46394 */
46395 Roo.ReaderLayout = function(config, renderTo){
46396     var c = config || {size:{}};
46397     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46398         north: c.north !== false ? Roo.apply({
46399             split:false,
46400             initialSize: 32,
46401             titlebar: false
46402         }, c.north) : false,
46403         west: c.west !== false ? Roo.apply({
46404             split:true,
46405             initialSize: 200,
46406             minSize: 175,
46407             maxSize: 400,
46408             titlebar: true,
46409             collapsible: true,
46410             animate: true,
46411             margins:{left:5,right:0,bottom:5,top:5},
46412             cmargins:{left:5,right:5,bottom:5,top:5}
46413         }, c.west) : false,
46414         east: c.east !== false ? Roo.apply({
46415             split:true,
46416             initialSize: 200,
46417             minSize: 175,
46418             maxSize: 400,
46419             titlebar: true,
46420             collapsible: true,
46421             animate: true,
46422             margins:{left:0,right:5,bottom:5,top:5},
46423             cmargins:{left:5,right:5,bottom:5,top:5}
46424         }, c.east) : false,
46425         center: Roo.apply({
46426             tabPosition: 'top',
46427             autoScroll:false,
46428             closeOnTab: true,
46429             titlebar:false,
46430             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46431         }, c.center)
46432     });
46433
46434     this.el.addClass('x-reader');
46435
46436     this.beginUpdate();
46437
46438     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46439         south: c.preview !== false ? Roo.apply({
46440             split:true,
46441             initialSize: 200,
46442             minSize: 100,
46443             autoScroll:true,
46444             collapsible:true,
46445             titlebar: true,
46446             cmargins:{top:5,left:0, right:0, bottom:0}
46447         }, c.preview) : false,
46448         center: Roo.apply({
46449             autoScroll:false,
46450             titlebar:false,
46451             minHeight:200
46452         }, c.listView)
46453     });
46454     this.add('center', new Roo.NestedLayoutPanel(inner,
46455             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46456
46457     this.endUpdate();
46458
46459     this.regions.preview = inner.getRegion('south');
46460     this.regions.listView = inner.getRegion('center');
46461 };
46462
46463 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46464  * Based on:
46465  * Ext JS Library 1.1.1
46466  * Copyright(c) 2006-2007, Ext JS, LLC.
46467  *
46468  * Originally Released Under LGPL - original licence link has changed is not relivant.
46469  *
46470  * Fork - LGPL
46471  * <script type="text/javascript">
46472  */
46473  
46474 /**
46475  * @class Roo.grid.Grid
46476  * @extends Roo.util.Observable
46477  * This class represents the primary interface of a component based grid control.
46478  * <br><br>Usage:<pre><code>
46479  var grid = new Roo.grid.Grid("my-container-id", {
46480      ds: myDataStore,
46481      cm: myColModel,
46482      selModel: mySelectionModel,
46483      autoSizeColumns: true,
46484      monitorWindowResize: false,
46485      trackMouseOver: true
46486  });
46487  // set any options
46488  grid.render();
46489  * </code></pre>
46490  * <b>Common Problems:</b><br/>
46491  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46492  * element will correct this<br/>
46493  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46494  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46495  * are unpredictable.<br/>
46496  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46497  * grid to calculate dimensions/offsets.<br/>
46498   * @constructor
46499  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46500  * The container MUST have some type of size defined for the grid to fill. The container will be
46501  * automatically set to position relative if it isn't already.
46502  * @param {Object} config A config object that sets properties on this grid.
46503  */
46504 Roo.grid.Grid = function(container, config){
46505         // initialize the container
46506         this.container = Roo.get(container);
46507         this.container.update("");
46508         this.container.setStyle("overflow", "hidden");
46509     this.container.addClass('x-grid-container');
46510
46511     this.id = this.container.id;
46512
46513     Roo.apply(this, config);
46514     // check and correct shorthanded configs
46515     if(this.ds){
46516         this.dataSource = this.ds;
46517         delete this.ds;
46518     }
46519     if(this.cm){
46520         this.colModel = this.cm;
46521         delete this.cm;
46522     }
46523     if(this.sm){
46524         this.selModel = this.sm;
46525         delete this.sm;
46526     }
46527
46528     if (this.selModel) {
46529         this.selModel = Roo.factory(this.selModel, Roo.grid);
46530         this.sm = this.selModel;
46531         this.sm.xmodule = this.xmodule || false;
46532     }
46533     if (typeof(this.colModel.config) == 'undefined') {
46534         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46535         this.cm = this.colModel;
46536         this.cm.xmodule = this.xmodule || false;
46537     }
46538     if (this.dataSource) {
46539         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46540         this.ds = this.dataSource;
46541         this.ds.xmodule = this.xmodule || false;
46542          
46543     }
46544     
46545     
46546     
46547     if(this.width){
46548         this.container.setWidth(this.width);
46549     }
46550
46551     if(this.height){
46552         this.container.setHeight(this.height);
46553     }
46554     /** @private */
46555         this.addEvents({
46556         // raw events
46557         /**
46558          * @event click
46559          * The raw click event for the entire grid.
46560          * @param {Roo.EventObject} e
46561          */
46562         "click" : true,
46563         /**
46564          * @event dblclick
46565          * The raw dblclick event for the entire grid.
46566          * @param {Roo.EventObject} e
46567          */
46568         "dblclick" : true,
46569         /**
46570          * @event contextmenu
46571          * The raw contextmenu event for the entire grid.
46572          * @param {Roo.EventObject} e
46573          */
46574         "contextmenu" : true,
46575         /**
46576          * @event mousedown
46577          * The raw mousedown event for the entire grid.
46578          * @param {Roo.EventObject} e
46579          */
46580         "mousedown" : true,
46581         /**
46582          * @event mouseup
46583          * The raw mouseup event for the entire grid.
46584          * @param {Roo.EventObject} e
46585          */
46586         "mouseup" : true,
46587         /**
46588          * @event mouseover
46589          * The raw mouseover event for the entire grid.
46590          * @param {Roo.EventObject} e
46591          */
46592         "mouseover" : true,
46593         /**
46594          * @event mouseout
46595          * The raw mouseout event for the entire grid.
46596          * @param {Roo.EventObject} e
46597          */
46598         "mouseout" : true,
46599         /**
46600          * @event keypress
46601          * The raw keypress event for the entire grid.
46602          * @param {Roo.EventObject} e
46603          */
46604         "keypress" : true,
46605         /**
46606          * @event keydown
46607          * The raw keydown event for the entire grid.
46608          * @param {Roo.EventObject} e
46609          */
46610         "keydown" : true,
46611
46612         // custom events
46613
46614         /**
46615          * @event cellclick
46616          * Fires when a cell is clicked
46617          * @param {Grid} this
46618          * @param {Number} rowIndex
46619          * @param {Number} columnIndex
46620          * @param {Roo.EventObject} e
46621          */
46622         "cellclick" : true,
46623         /**
46624          * @event celldblclick
46625          * Fires when a cell is double clicked
46626          * @param {Grid} this
46627          * @param {Number} rowIndex
46628          * @param {Number} columnIndex
46629          * @param {Roo.EventObject} e
46630          */
46631         "celldblclick" : true,
46632         /**
46633          * @event rowclick
46634          * Fires when a row is clicked
46635          * @param {Grid} this
46636          * @param {Number} rowIndex
46637          * @param {Roo.EventObject} e
46638          */
46639         "rowclick" : true,
46640         /**
46641          * @event rowdblclick
46642          * Fires when a row is double clicked
46643          * @param {Grid} this
46644          * @param {Number} rowIndex
46645          * @param {Roo.EventObject} e
46646          */
46647         "rowdblclick" : true,
46648         /**
46649          * @event headerclick
46650          * Fires when a header is clicked
46651          * @param {Grid} this
46652          * @param {Number} columnIndex
46653          * @param {Roo.EventObject} e
46654          */
46655         "headerclick" : true,
46656         /**
46657          * @event headerdblclick
46658          * Fires when a header cell is double clicked
46659          * @param {Grid} this
46660          * @param {Number} columnIndex
46661          * @param {Roo.EventObject} e
46662          */
46663         "headerdblclick" : true,
46664         /**
46665          * @event rowcontextmenu
46666          * Fires when a row is right clicked
46667          * @param {Grid} this
46668          * @param {Number} rowIndex
46669          * @param {Roo.EventObject} e
46670          */
46671         "rowcontextmenu" : true,
46672         /**
46673          * @event cellcontextmenu
46674          * Fires when a cell is right clicked
46675          * @param {Grid} this
46676          * @param {Number} rowIndex
46677          * @param {Number} cellIndex
46678          * @param {Roo.EventObject} e
46679          */
46680          "cellcontextmenu" : true,
46681         /**
46682          * @event headercontextmenu
46683          * Fires when a header is right clicked
46684          * @param {Grid} this
46685          * @param {Number} columnIndex
46686          * @param {Roo.EventObject} e
46687          */
46688         "headercontextmenu" : true,
46689         /**
46690          * @event bodyscroll
46691          * Fires when the body element is scrolled
46692          * @param {Number} scrollLeft
46693          * @param {Number} scrollTop
46694          */
46695         "bodyscroll" : true,
46696         /**
46697          * @event columnresize
46698          * Fires when the user resizes a column
46699          * @param {Number} columnIndex
46700          * @param {Number} newSize
46701          */
46702         "columnresize" : true,
46703         /**
46704          * @event columnmove
46705          * Fires when the user moves a column
46706          * @param {Number} oldIndex
46707          * @param {Number} newIndex
46708          */
46709         "columnmove" : true,
46710         /**
46711          * @event startdrag
46712          * Fires when row(s) start being dragged
46713          * @param {Grid} this
46714          * @param {Roo.GridDD} dd The drag drop object
46715          * @param {event} e The raw browser event
46716          */
46717         "startdrag" : true,
46718         /**
46719          * @event enddrag
46720          * Fires when a drag operation is complete
46721          * @param {Grid} this
46722          * @param {Roo.GridDD} dd The drag drop object
46723          * @param {event} e The raw browser event
46724          */
46725         "enddrag" : true,
46726         /**
46727          * @event dragdrop
46728          * Fires when dragged row(s) are dropped on a valid DD target
46729          * @param {Grid} this
46730          * @param {Roo.GridDD} dd The drag drop object
46731          * @param {String} targetId The target drag drop object
46732          * @param {event} e The raw browser event
46733          */
46734         "dragdrop" : true,
46735         /**
46736          * @event dragover
46737          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46738          * @param {Grid} this
46739          * @param {Roo.GridDD} dd The drag drop object
46740          * @param {String} targetId The target drag drop object
46741          * @param {event} e The raw browser event
46742          */
46743         "dragover" : true,
46744         /**
46745          * @event dragenter
46746          *  Fires when the dragged row(s) first cross another DD target while being dragged
46747          * @param {Grid} this
46748          * @param {Roo.GridDD} dd The drag drop object
46749          * @param {String} targetId The target drag drop object
46750          * @param {event} e The raw browser event
46751          */
46752         "dragenter" : true,
46753         /**
46754          * @event dragout
46755          * Fires when the dragged row(s) leave another DD target while being dragged
46756          * @param {Grid} this
46757          * @param {Roo.GridDD} dd The drag drop object
46758          * @param {String} targetId The target drag drop object
46759          * @param {event} e The raw browser event
46760          */
46761         "dragout" : true,
46762         /**
46763          * @event rowclass
46764          * Fires when a row is rendered, so you can change add a style to it.
46765          * @param {GridView} gridview   The grid view
46766          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
46767          */
46768         'rowclass' : true,
46769
46770         /**
46771          * @event render
46772          * Fires when the grid is rendered
46773          * @param {Grid} grid
46774          */
46775         'render' : true
46776     });
46777
46778     Roo.grid.Grid.superclass.constructor.call(this);
46779 };
46780 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46781     
46782     /**
46783      * @cfg {String} ddGroup - drag drop group.
46784      */
46785
46786     /**
46787      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46788      */
46789     minColumnWidth : 25,
46790
46791     /**
46792      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46793      * <b>on initial render.</b> It is more efficient to explicitly size the columns
46794      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46795      */
46796     autoSizeColumns : false,
46797
46798     /**
46799      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46800      */
46801     autoSizeHeaders : true,
46802
46803     /**
46804      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46805      */
46806     monitorWindowResize : true,
46807
46808     /**
46809      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46810      * rows measured to get a columns size. Default is 0 (all rows).
46811      */
46812     maxRowsToMeasure : 0,
46813
46814     /**
46815      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46816      */
46817     trackMouseOver : true,
46818
46819     /**
46820     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46821     */
46822     
46823     /**
46824     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46825     */
46826     enableDragDrop : false,
46827     
46828     /**
46829     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46830     */
46831     enableColumnMove : true,
46832     
46833     /**
46834     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46835     */
46836     enableColumnHide : true,
46837     
46838     /**
46839     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46840     */
46841     enableRowHeightSync : false,
46842     
46843     /**
46844     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46845     */
46846     stripeRows : true,
46847     
46848     /**
46849     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46850     */
46851     autoHeight : false,
46852
46853     /**
46854      * @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.
46855      */
46856     autoExpandColumn : false,
46857
46858     /**
46859     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46860     * Default is 50.
46861     */
46862     autoExpandMin : 50,
46863
46864     /**
46865     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46866     */
46867     autoExpandMax : 1000,
46868
46869     /**
46870     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46871     */
46872     view : null,
46873
46874     /**
46875     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46876     */
46877     loadMask : false,
46878     /**
46879     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46880     */
46881     dropTarget: false,
46882     
46883    
46884     
46885     // private
46886     rendered : false,
46887
46888     /**
46889     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46890     * of a fixed width. Default is false.
46891     */
46892     /**
46893     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46894     */
46895     /**
46896      * Called once after all setup has been completed and the grid is ready to be rendered.
46897      * @return {Roo.grid.Grid} this
46898      */
46899     render : function()
46900     {
46901         var c = this.container;
46902         // try to detect autoHeight/width mode
46903         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46904             this.autoHeight = true;
46905         }
46906         var view = this.getView();
46907         view.init(this);
46908
46909         c.on("click", this.onClick, this);
46910         c.on("dblclick", this.onDblClick, this);
46911         c.on("contextmenu", this.onContextMenu, this);
46912         c.on("keydown", this.onKeyDown, this);
46913
46914         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46915
46916         this.getSelectionModel().init(this);
46917
46918         view.render();
46919
46920         if(this.loadMask){
46921             this.loadMask = new Roo.LoadMask(this.container,
46922                     Roo.apply({store:this.dataSource}, this.loadMask));
46923         }
46924         
46925         
46926         if (this.toolbar && this.toolbar.xtype) {
46927             this.toolbar.container = this.getView().getHeaderPanel(true);
46928             this.toolbar = new Roo.Toolbar(this.toolbar);
46929         }
46930         if (this.footer && this.footer.xtype) {
46931             this.footer.dataSource = this.getDataSource();
46932             this.footer.container = this.getView().getFooterPanel(true);
46933             this.footer = Roo.factory(this.footer, Roo);
46934         }
46935         if (this.dropTarget && this.dropTarget.xtype) {
46936             delete this.dropTarget.xtype;
46937             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46938         }
46939         
46940         
46941         this.rendered = true;
46942         this.fireEvent('render', this);
46943         return this;
46944     },
46945
46946         /**
46947          * Reconfigures the grid to use a different Store and Column Model.
46948          * The View will be bound to the new objects and refreshed.
46949          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46950          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46951          */
46952     reconfigure : function(dataSource, colModel){
46953         if(this.loadMask){
46954             this.loadMask.destroy();
46955             this.loadMask = new Roo.LoadMask(this.container,
46956                     Roo.apply({store:dataSource}, this.loadMask));
46957         }
46958         this.view.bind(dataSource, colModel);
46959         this.dataSource = dataSource;
46960         this.colModel = colModel;
46961         this.view.refresh(true);
46962     },
46963
46964     // private
46965     onKeyDown : function(e){
46966         this.fireEvent("keydown", e);
46967     },
46968
46969     /**
46970      * Destroy this grid.
46971      * @param {Boolean} removeEl True to remove the element
46972      */
46973     destroy : function(removeEl, keepListeners){
46974         if(this.loadMask){
46975             this.loadMask.destroy();
46976         }
46977         var c = this.container;
46978         c.removeAllListeners();
46979         this.view.destroy();
46980         this.colModel.purgeListeners();
46981         if(!keepListeners){
46982             this.purgeListeners();
46983         }
46984         c.update("");
46985         if(removeEl === true){
46986             c.remove();
46987         }
46988     },
46989
46990     // private
46991     processEvent : function(name, e){
46992         this.fireEvent(name, e);
46993         var t = e.getTarget();
46994         var v = this.view;
46995         var header = v.findHeaderIndex(t);
46996         if(header !== false){
46997             this.fireEvent("header" + name, this, header, e);
46998         }else{
46999             var row = v.findRowIndex(t);
47000             var cell = v.findCellIndex(t);
47001             if(row !== false){
47002                 this.fireEvent("row" + name, this, row, e);
47003                 if(cell !== false){
47004                     this.fireEvent("cell" + name, this, row, cell, e);
47005                 }
47006             }
47007         }
47008     },
47009
47010     // private
47011     onClick : function(e){
47012         this.processEvent("click", e);
47013     },
47014
47015     // private
47016     onContextMenu : function(e, t){
47017         this.processEvent("contextmenu", e);
47018     },
47019
47020     // private
47021     onDblClick : function(e){
47022         this.processEvent("dblclick", e);
47023     },
47024
47025     // private
47026     walkCells : function(row, col, step, fn, scope){
47027         var cm = this.colModel, clen = cm.getColumnCount();
47028         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47029         if(step < 0){
47030             if(col < 0){
47031                 row--;
47032                 first = false;
47033             }
47034             while(row >= 0){
47035                 if(!first){
47036                     col = clen-1;
47037                 }
47038                 first = false;
47039                 while(col >= 0){
47040                     if(fn.call(scope || this, row, col, cm) === true){
47041                         return [row, col];
47042                     }
47043                     col--;
47044                 }
47045                 row--;
47046             }
47047         } else {
47048             if(col >= clen){
47049                 row++;
47050                 first = false;
47051             }
47052             while(row < rlen){
47053                 if(!first){
47054                     col = 0;
47055                 }
47056                 first = false;
47057                 while(col < clen){
47058                     if(fn.call(scope || this, row, col, cm) === true){
47059                         return [row, col];
47060                     }
47061                     col++;
47062                 }
47063                 row++;
47064             }
47065         }
47066         return null;
47067     },
47068
47069     // private
47070     getSelections : function(){
47071         return this.selModel.getSelections();
47072     },
47073
47074     /**
47075      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47076      * but if manual update is required this method will initiate it.
47077      */
47078     autoSize : function(){
47079         if(this.rendered){
47080             this.view.layout();
47081             if(this.view.adjustForScroll){
47082                 this.view.adjustForScroll();
47083             }
47084         }
47085     },
47086
47087     /**
47088      * Returns the grid's underlying element.
47089      * @return {Element} The element
47090      */
47091     getGridEl : function(){
47092         return this.container;
47093     },
47094
47095     // private for compatibility, overridden by editor grid
47096     stopEditing : function(){},
47097
47098     /**
47099      * Returns the grid's SelectionModel.
47100      * @return {SelectionModel}
47101      */
47102     getSelectionModel : function(){
47103         if(!this.selModel){
47104             this.selModel = new Roo.grid.RowSelectionModel();
47105         }
47106         return this.selModel;
47107     },
47108
47109     /**
47110      * Returns the grid's DataSource.
47111      * @return {DataSource}
47112      */
47113     getDataSource : function(){
47114         return this.dataSource;
47115     },
47116
47117     /**
47118      * Returns the grid's ColumnModel.
47119      * @return {ColumnModel}
47120      */
47121     getColumnModel : function(){
47122         return this.colModel;
47123     },
47124
47125     /**
47126      * Returns the grid's GridView object.
47127      * @return {GridView}
47128      */
47129     getView : function(){
47130         if(!this.view){
47131             this.view = new Roo.grid.GridView(this.viewConfig);
47132         }
47133         return this.view;
47134     },
47135     /**
47136      * Called to get grid's drag proxy text, by default returns this.ddText.
47137      * @return {String}
47138      */
47139     getDragDropText : function(){
47140         var count = this.selModel.getCount();
47141         return String.format(this.ddText, count, count == 1 ? '' : 's');
47142     }
47143 });
47144 /**
47145  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47146  * %0 is replaced with the number of selected rows.
47147  * @type String
47148  */
47149 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47150  * Based on:
47151  * Ext JS Library 1.1.1
47152  * Copyright(c) 2006-2007, Ext JS, LLC.
47153  *
47154  * Originally Released Under LGPL - original licence link has changed is not relivant.
47155  *
47156  * Fork - LGPL
47157  * <script type="text/javascript">
47158  */
47159  
47160 Roo.grid.AbstractGridView = function(){
47161         this.grid = null;
47162         
47163         this.events = {
47164             "beforerowremoved" : true,
47165             "beforerowsinserted" : true,
47166             "beforerefresh" : true,
47167             "rowremoved" : true,
47168             "rowsinserted" : true,
47169             "rowupdated" : true,
47170             "refresh" : true
47171         };
47172     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47173 };
47174
47175 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47176     rowClass : "x-grid-row",
47177     cellClass : "x-grid-cell",
47178     tdClass : "x-grid-td",
47179     hdClass : "x-grid-hd",
47180     splitClass : "x-grid-hd-split",
47181     
47182         init: function(grid){
47183         this.grid = grid;
47184                 var cid = this.grid.getGridEl().id;
47185         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47186         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47187         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47188         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47189         },
47190         
47191         getColumnRenderers : function(){
47192         var renderers = [];
47193         var cm = this.grid.colModel;
47194         var colCount = cm.getColumnCount();
47195         for(var i = 0; i < colCount; i++){
47196             renderers[i] = cm.getRenderer(i);
47197         }
47198         return renderers;
47199     },
47200     
47201     getColumnIds : function(){
47202         var ids = [];
47203         var cm = this.grid.colModel;
47204         var colCount = cm.getColumnCount();
47205         for(var i = 0; i < colCount; i++){
47206             ids[i] = cm.getColumnId(i);
47207         }
47208         return ids;
47209     },
47210     
47211     getDataIndexes : function(){
47212         if(!this.indexMap){
47213             this.indexMap = this.buildIndexMap();
47214         }
47215         return this.indexMap.colToData;
47216     },
47217     
47218     getColumnIndexByDataIndex : function(dataIndex){
47219         if(!this.indexMap){
47220             this.indexMap = this.buildIndexMap();
47221         }
47222         return this.indexMap.dataToCol[dataIndex];
47223     },
47224     
47225     /**
47226      * Set a css style for a column dynamically. 
47227      * @param {Number} colIndex The index of the column
47228      * @param {String} name The css property name
47229      * @param {String} value The css value
47230      */
47231     setCSSStyle : function(colIndex, name, value){
47232         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
47233         Roo.util.CSS.updateRule(selector, name, value);
47234     },
47235     
47236     generateRules : function(cm){
47237         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
47238         Roo.util.CSS.removeStyleSheet(rulesId);
47239         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47240             var cid = cm.getColumnId(i);
47241             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
47242                          this.tdSelector, cid, " {\n}\n",
47243                          this.hdSelector, cid, " {\n}\n",
47244                          this.splitSelector, cid, " {\n}\n");
47245         }
47246         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47247     }
47248 });/*
47249  * Based on:
47250  * Ext JS Library 1.1.1
47251  * Copyright(c) 2006-2007, Ext JS, LLC.
47252  *
47253  * Originally Released Under LGPL - original licence link has changed is not relivant.
47254  *
47255  * Fork - LGPL
47256  * <script type="text/javascript">
47257  */
47258
47259 // private
47260 // This is a support class used internally by the Grid components
47261 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
47262     this.grid = grid;
47263     this.view = grid.getView();
47264     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47265     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
47266     if(hd2){
47267         this.setHandleElId(Roo.id(hd));
47268         this.setOuterHandleElId(Roo.id(hd2));
47269     }
47270     this.scroll = false;
47271 };
47272 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
47273     maxDragWidth: 120,
47274     getDragData : function(e){
47275         var t = Roo.lib.Event.getTarget(e);
47276         var h = this.view.findHeaderCell(t);
47277         if(h){
47278             return {ddel: h.firstChild, header:h};
47279         }
47280         return false;
47281     },
47282
47283     onInitDrag : function(e){
47284         this.view.headersDisabled = true;
47285         var clone = this.dragData.ddel.cloneNode(true);
47286         clone.id = Roo.id();
47287         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
47288         this.proxy.update(clone);
47289         return true;
47290     },
47291
47292     afterValidDrop : function(){
47293         var v = this.view;
47294         setTimeout(function(){
47295             v.headersDisabled = false;
47296         }, 50);
47297     },
47298
47299     afterInvalidDrop : function(){
47300         var v = this.view;
47301         setTimeout(function(){
47302             v.headersDisabled = false;
47303         }, 50);
47304     }
47305 });
47306 /*
47307  * Based on:
47308  * Ext JS Library 1.1.1
47309  * Copyright(c) 2006-2007, Ext JS, LLC.
47310  *
47311  * Originally Released Under LGPL - original licence link has changed is not relivant.
47312  *
47313  * Fork - LGPL
47314  * <script type="text/javascript">
47315  */
47316 // private
47317 // This is a support class used internally by the Grid components
47318 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
47319     this.grid = grid;
47320     this.view = grid.getView();
47321     // split the proxies so they don't interfere with mouse events
47322     this.proxyTop = Roo.DomHelper.append(document.body, {
47323         cls:"col-move-top", html:"&#160;"
47324     }, true);
47325     this.proxyBottom = Roo.DomHelper.append(document.body, {
47326         cls:"col-move-bottom", html:"&#160;"
47327     }, true);
47328     this.proxyTop.hide = this.proxyBottom.hide = function(){
47329         this.setLeftTop(-100,-100);
47330         this.setStyle("visibility", "hidden");
47331     };
47332     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
47333     // temporarily disabled
47334     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
47335     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
47336 };
47337 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
47338     proxyOffsets : [-4, -9],
47339     fly: Roo.Element.fly,
47340
47341     getTargetFromEvent : function(e){
47342         var t = Roo.lib.Event.getTarget(e);
47343         var cindex = this.view.findCellIndex(t);
47344         if(cindex !== false){
47345             return this.view.getHeaderCell(cindex);
47346         }
47347         return null;
47348     },
47349
47350     nextVisible : function(h){
47351         var v = this.view, cm = this.grid.colModel;
47352         h = h.nextSibling;
47353         while(h){
47354             if(!cm.isHidden(v.getCellIndex(h))){
47355                 return h;
47356             }
47357             h = h.nextSibling;
47358         }
47359         return null;
47360     },
47361
47362     prevVisible : function(h){
47363         var v = this.view, cm = this.grid.colModel;
47364         h = h.prevSibling;
47365         while(h){
47366             if(!cm.isHidden(v.getCellIndex(h))){
47367                 return h;
47368             }
47369             h = h.prevSibling;
47370         }
47371         return null;
47372     },
47373
47374     positionIndicator : function(h, n, e){
47375         var x = Roo.lib.Event.getPageX(e);
47376         var r = Roo.lib.Dom.getRegion(n.firstChild);
47377         var px, pt, py = r.top + this.proxyOffsets[1];
47378         if((r.right - x) <= (r.right-r.left)/2){
47379             px = r.right+this.view.borderWidth;
47380             pt = "after";
47381         }else{
47382             px = r.left;
47383             pt = "before";
47384         }
47385         var oldIndex = this.view.getCellIndex(h);
47386         var newIndex = this.view.getCellIndex(n);
47387
47388         if(this.grid.colModel.isFixed(newIndex)){
47389             return false;
47390         }
47391
47392         var locked = this.grid.colModel.isLocked(newIndex);
47393
47394         if(pt == "after"){
47395             newIndex++;
47396         }
47397         if(oldIndex < newIndex){
47398             newIndex--;
47399         }
47400         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47401             return false;
47402         }
47403         px +=  this.proxyOffsets[0];
47404         this.proxyTop.setLeftTop(px, py);
47405         this.proxyTop.show();
47406         if(!this.bottomOffset){
47407             this.bottomOffset = this.view.mainHd.getHeight();
47408         }
47409         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47410         this.proxyBottom.show();
47411         return pt;
47412     },
47413
47414     onNodeEnter : function(n, dd, e, data){
47415         if(data.header != n){
47416             this.positionIndicator(data.header, n, e);
47417         }
47418     },
47419
47420     onNodeOver : function(n, dd, e, data){
47421         var result = false;
47422         if(data.header != n){
47423             result = this.positionIndicator(data.header, n, e);
47424         }
47425         if(!result){
47426             this.proxyTop.hide();
47427             this.proxyBottom.hide();
47428         }
47429         return result ? this.dropAllowed : this.dropNotAllowed;
47430     },
47431
47432     onNodeOut : function(n, dd, e, data){
47433         this.proxyTop.hide();
47434         this.proxyBottom.hide();
47435     },
47436
47437     onNodeDrop : function(n, dd, e, data){
47438         var h = data.header;
47439         if(h != n){
47440             var cm = this.grid.colModel;
47441             var x = Roo.lib.Event.getPageX(e);
47442             var r = Roo.lib.Dom.getRegion(n.firstChild);
47443             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47444             var oldIndex = this.view.getCellIndex(h);
47445             var newIndex = this.view.getCellIndex(n);
47446             var locked = cm.isLocked(newIndex);
47447             if(pt == "after"){
47448                 newIndex++;
47449             }
47450             if(oldIndex < newIndex){
47451                 newIndex--;
47452             }
47453             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47454                 return false;
47455             }
47456             cm.setLocked(oldIndex, locked, true);
47457             cm.moveColumn(oldIndex, newIndex);
47458             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47459             return true;
47460         }
47461         return false;
47462     }
47463 });
47464 /*
47465  * Based on:
47466  * Ext JS Library 1.1.1
47467  * Copyright(c) 2006-2007, Ext JS, LLC.
47468  *
47469  * Originally Released Under LGPL - original licence link has changed is not relivant.
47470  *
47471  * Fork - LGPL
47472  * <script type="text/javascript">
47473  */
47474   
47475 /**
47476  * @class Roo.grid.GridView
47477  * @extends Roo.util.Observable
47478  *
47479  * @constructor
47480  * @param {Object} config
47481  */
47482 Roo.grid.GridView = function(config){
47483     Roo.grid.GridView.superclass.constructor.call(this);
47484     this.el = null;
47485
47486     Roo.apply(this, config);
47487 };
47488
47489 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47490
47491     /**
47492      * Override this function to apply custom css classes to rows during rendering
47493      * @param {Record} record The record
47494      * @param {Number} index
47495      * @method getRowClass
47496      */
47497     rowClass : "x-grid-row",
47498
47499     cellClass : "x-grid-col",
47500
47501     tdClass : "x-grid-td",
47502
47503     hdClass : "x-grid-hd",
47504
47505     splitClass : "x-grid-split",
47506
47507     sortClasses : ["sort-asc", "sort-desc"],
47508
47509     enableMoveAnim : false,
47510
47511     hlColor: "C3DAF9",
47512
47513     dh : Roo.DomHelper,
47514
47515     fly : Roo.Element.fly,
47516
47517     css : Roo.util.CSS,
47518
47519     borderWidth: 1,
47520
47521     splitOffset: 3,
47522
47523     scrollIncrement : 22,
47524
47525     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47526
47527     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47528
47529     bind : function(ds, cm){
47530         if(this.ds){
47531             this.ds.un("load", this.onLoad, this);
47532             this.ds.un("datachanged", this.onDataChange, this);
47533             this.ds.un("add", this.onAdd, this);
47534             this.ds.un("remove", this.onRemove, this);
47535             this.ds.un("update", this.onUpdate, this);
47536             this.ds.un("clear", this.onClear, this);
47537         }
47538         if(ds){
47539             ds.on("load", this.onLoad, this);
47540             ds.on("datachanged", this.onDataChange, this);
47541             ds.on("add", this.onAdd, this);
47542             ds.on("remove", this.onRemove, this);
47543             ds.on("update", this.onUpdate, this);
47544             ds.on("clear", this.onClear, this);
47545         }
47546         this.ds = ds;
47547
47548         if(this.cm){
47549             this.cm.un("widthchange", this.onColWidthChange, this);
47550             this.cm.un("headerchange", this.onHeaderChange, this);
47551             this.cm.un("hiddenchange", this.onHiddenChange, this);
47552             this.cm.un("columnmoved", this.onColumnMove, this);
47553             this.cm.un("columnlockchange", this.onColumnLock, this);
47554         }
47555         if(cm){
47556             this.generateRules(cm);
47557             cm.on("widthchange", this.onColWidthChange, this);
47558             cm.on("headerchange", this.onHeaderChange, this);
47559             cm.on("hiddenchange", this.onHiddenChange, this);
47560             cm.on("columnmoved", this.onColumnMove, this);
47561             cm.on("columnlockchange", this.onColumnLock, this);
47562         }
47563         this.cm = cm;
47564     },
47565
47566     init: function(grid){
47567         Roo.grid.GridView.superclass.init.call(this, grid);
47568
47569         this.bind(grid.dataSource, grid.colModel);
47570
47571         grid.on("headerclick", this.handleHeaderClick, this);
47572
47573         if(grid.trackMouseOver){
47574             grid.on("mouseover", this.onRowOver, this);
47575             grid.on("mouseout", this.onRowOut, this);
47576         }
47577         grid.cancelTextSelection = function(){};
47578         this.gridId = grid.id;
47579
47580         var tpls = this.templates || {};
47581
47582         if(!tpls.master){
47583             tpls.master = new Roo.Template(
47584                '<div class="x-grid" hidefocus="true">',
47585                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47586                   '<div class="x-grid-topbar"></div>',
47587                   '<div class="x-grid-scroller"><div></div></div>',
47588                   '<div class="x-grid-locked">',
47589                       '<div class="x-grid-header">{lockedHeader}</div>',
47590                       '<div class="x-grid-body">{lockedBody}</div>',
47591                   "</div>",
47592                   '<div class="x-grid-viewport">',
47593                       '<div class="x-grid-header">{header}</div>',
47594                       '<div class="x-grid-body">{body}</div>',
47595                   "</div>",
47596                   '<div class="x-grid-bottombar"></div>',
47597                  
47598                   '<div class="x-grid-resize-proxy">&#160;</div>',
47599                "</div>"
47600             );
47601             tpls.master.disableformats = true;
47602         }
47603
47604         if(!tpls.header){
47605             tpls.header = new Roo.Template(
47606                '<table border="0" cellspacing="0" cellpadding="0">',
47607                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47608                "</table>{splits}"
47609             );
47610             tpls.header.disableformats = true;
47611         }
47612         tpls.header.compile();
47613
47614         if(!tpls.hcell){
47615             tpls.hcell = new Roo.Template(
47616                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47617                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47618                 "</div></td>"
47619              );
47620              tpls.hcell.disableFormats = true;
47621         }
47622         tpls.hcell.compile();
47623
47624         if(!tpls.hsplit){
47625             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47626             tpls.hsplit.disableFormats = true;
47627         }
47628         tpls.hsplit.compile();
47629
47630         if(!tpls.body){
47631             tpls.body = new Roo.Template(
47632                '<table border="0" cellspacing="0" cellpadding="0">',
47633                "<tbody>{rows}</tbody>",
47634                "</table>"
47635             );
47636             tpls.body.disableFormats = true;
47637         }
47638         tpls.body.compile();
47639
47640         if(!tpls.row){
47641             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47642             tpls.row.disableFormats = true;
47643         }
47644         tpls.row.compile();
47645
47646         if(!tpls.cell){
47647             tpls.cell = new Roo.Template(
47648                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47649                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47650                 "</td>"
47651             );
47652             tpls.cell.disableFormats = true;
47653         }
47654         tpls.cell.compile();
47655
47656         this.templates = tpls;
47657     },
47658
47659     // remap these for backwards compat
47660     onColWidthChange : function(){
47661         this.updateColumns.apply(this, arguments);
47662     },
47663     onHeaderChange : function(){
47664         this.updateHeaders.apply(this, arguments);
47665     }, 
47666     onHiddenChange : function(){
47667         this.handleHiddenChange.apply(this, arguments);
47668     },
47669     onColumnMove : function(){
47670         this.handleColumnMove.apply(this, arguments);
47671     },
47672     onColumnLock : function(){
47673         this.handleLockChange.apply(this, arguments);
47674     },
47675
47676     onDataChange : function(){
47677         this.refresh();
47678         this.updateHeaderSortState();
47679     },
47680
47681     onClear : function(){
47682         this.refresh();
47683     },
47684
47685     onUpdate : function(ds, record){
47686         this.refreshRow(record);
47687     },
47688
47689     refreshRow : function(record){
47690         var ds = this.ds, index;
47691         if(typeof record == 'number'){
47692             index = record;
47693             record = ds.getAt(index);
47694         }else{
47695             index = ds.indexOf(record);
47696         }
47697         this.insertRows(ds, index, index, true);
47698         this.onRemove(ds, record, index+1, true);
47699         this.syncRowHeights(index, index);
47700         this.layout();
47701         this.fireEvent("rowupdated", this, index, record);
47702     },
47703
47704     onAdd : function(ds, records, index){
47705         this.insertRows(ds, index, index + (records.length-1));
47706     },
47707
47708     onRemove : function(ds, record, index, isUpdate){
47709         if(isUpdate !== true){
47710             this.fireEvent("beforerowremoved", this, index, record);
47711         }
47712         var bt = this.getBodyTable(), lt = this.getLockedTable();
47713         if(bt.rows[index]){
47714             bt.firstChild.removeChild(bt.rows[index]);
47715         }
47716         if(lt.rows[index]){
47717             lt.firstChild.removeChild(lt.rows[index]);
47718         }
47719         if(isUpdate !== true){
47720             this.stripeRows(index);
47721             this.syncRowHeights(index, index);
47722             this.layout();
47723             this.fireEvent("rowremoved", this, index, record);
47724         }
47725     },
47726
47727     onLoad : function(){
47728         this.scrollToTop();
47729     },
47730
47731     /**
47732      * Scrolls the grid to the top
47733      */
47734     scrollToTop : function(){
47735         if(this.scroller){
47736             this.scroller.dom.scrollTop = 0;
47737             this.syncScroll();
47738         }
47739     },
47740
47741     /**
47742      * Gets a panel in the header of the grid that can be used for toolbars etc.
47743      * After modifying the contents of this panel a call to grid.autoSize() may be
47744      * required to register any changes in size.
47745      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47746      * @return Roo.Element
47747      */
47748     getHeaderPanel : function(doShow){
47749         if(doShow){
47750             this.headerPanel.show();
47751         }
47752         return this.headerPanel;
47753     },
47754
47755     /**
47756      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47757      * After modifying the contents of this panel a call to grid.autoSize() may be
47758      * required to register any changes in size.
47759      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47760      * @return Roo.Element
47761      */
47762     getFooterPanel : function(doShow){
47763         if(doShow){
47764             this.footerPanel.show();
47765         }
47766         return this.footerPanel;
47767     },
47768
47769     initElements : function(){
47770         var E = Roo.Element;
47771         var el = this.grid.getGridEl().dom.firstChild;
47772         var cs = el.childNodes;
47773
47774         this.el = new E(el);
47775         
47776          this.focusEl = new E(el.firstChild);
47777         this.focusEl.swallowEvent("click", true);
47778         
47779         this.headerPanel = new E(cs[1]);
47780         this.headerPanel.enableDisplayMode("block");
47781
47782         this.scroller = new E(cs[2]);
47783         this.scrollSizer = new E(this.scroller.dom.firstChild);
47784
47785         this.lockedWrap = new E(cs[3]);
47786         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47787         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47788
47789         this.mainWrap = new E(cs[4]);
47790         this.mainHd = new E(this.mainWrap.dom.firstChild);
47791         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47792
47793         this.footerPanel = new E(cs[5]);
47794         this.footerPanel.enableDisplayMode("block");
47795
47796         this.resizeProxy = new E(cs[6]);
47797
47798         this.headerSelector = String.format(
47799            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47800            this.lockedHd.id, this.mainHd.id
47801         );
47802
47803         this.splitterSelector = String.format(
47804            '#{0} div.x-grid-split, #{1} div.x-grid-split',
47805            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47806         );
47807     },
47808     idToCssName : function(s)
47809     {
47810         return s.replace(/[^a-z0-9]+/ig, '-');
47811     },
47812
47813     getHeaderCell : function(index){
47814         return Roo.DomQuery.select(this.headerSelector)[index];
47815     },
47816
47817     getHeaderCellMeasure : function(index){
47818         return this.getHeaderCell(index).firstChild;
47819     },
47820
47821     getHeaderCellText : function(index){
47822         return this.getHeaderCell(index).firstChild.firstChild;
47823     },
47824
47825     getLockedTable : function(){
47826         return this.lockedBody.dom.firstChild;
47827     },
47828
47829     getBodyTable : function(){
47830         return this.mainBody.dom.firstChild;
47831     },
47832
47833     getLockedRow : function(index){
47834         return this.getLockedTable().rows[index];
47835     },
47836
47837     getRow : function(index){
47838         return this.getBodyTable().rows[index];
47839     },
47840
47841     getRowComposite : function(index){
47842         if(!this.rowEl){
47843             this.rowEl = new Roo.CompositeElementLite();
47844         }
47845         var els = [], lrow, mrow;
47846         if(lrow = this.getLockedRow(index)){
47847             els.push(lrow);
47848         }
47849         if(mrow = this.getRow(index)){
47850             els.push(mrow);
47851         }
47852         this.rowEl.elements = els;
47853         return this.rowEl;
47854     },
47855
47856     getCell : function(rowIndex, colIndex){
47857         var locked = this.cm.getLockedCount();
47858         var source;
47859         if(colIndex < locked){
47860             source = this.lockedBody.dom.firstChild;
47861         }else{
47862             source = this.mainBody.dom.firstChild;
47863             colIndex -= locked;
47864         }
47865         return source.rows[rowIndex].childNodes[colIndex];
47866     },
47867
47868     getCellText : function(rowIndex, colIndex){
47869         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47870     },
47871
47872     getCellBox : function(cell){
47873         var b = this.fly(cell).getBox();
47874         if(Roo.isOpera){ // opera fails to report the Y
47875             b.y = cell.offsetTop + this.mainBody.getY();
47876         }
47877         return b;
47878     },
47879
47880     getCellIndex : function(cell){
47881         var id = String(cell.className).match(this.cellRE);
47882         if(id){
47883             return parseInt(id[1], 10);
47884         }
47885         return 0;
47886     },
47887
47888     findHeaderIndex : function(n){
47889         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47890         return r ? this.getCellIndex(r) : false;
47891     },
47892
47893     findHeaderCell : function(n){
47894         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47895         return r ? r : false;
47896     },
47897
47898     findRowIndex : function(n){
47899         if(!n){
47900             return false;
47901         }
47902         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47903         return r ? r.rowIndex : false;
47904     },
47905
47906     findCellIndex : function(node){
47907         var stop = this.el.dom;
47908         while(node && node != stop){
47909             if(this.findRE.test(node.className)){
47910                 return this.getCellIndex(node);
47911             }
47912             node = node.parentNode;
47913         }
47914         return false;
47915     },
47916
47917     getColumnId : function(index){
47918         return this.cm.getColumnId(index);
47919     },
47920
47921     getSplitters : function()
47922     {
47923         if(this.splitterSelector){
47924            return Roo.DomQuery.select(this.splitterSelector);
47925         }else{
47926             return null;
47927       }
47928     },
47929
47930     getSplitter : function(index){
47931         return this.getSplitters()[index];
47932     },
47933
47934     onRowOver : function(e, t){
47935         var row;
47936         if((row = this.findRowIndex(t)) !== false){
47937             this.getRowComposite(row).addClass("x-grid-row-over");
47938         }
47939     },
47940
47941     onRowOut : function(e, t){
47942         var row;
47943         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47944             this.getRowComposite(row).removeClass("x-grid-row-over");
47945         }
47946     },
47947
47948     renderHeaders : function(){
47949         var cm = this.cm;
47950         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47951         var cb = [], lb = [], sb = [], lsb = [], p = {};
47952         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47953             p.cellId = "x-grid-hd-0-" + i;
47954             p.splitId = "x-grid-csplit-0-" + i;
47955             p.id = cm.getColumnId(i);
47956             p.title = cm.getColumnTooltip(i) || "";
47957             p.value = cm.getColumnHeader(i) || "";
47958             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47959             if(!cm.isLocked(i)){
47960                 cb[cb.length] = ct.apply(p);
47961                 sb[sb.length] = st.apply(p);
47962             }else{
47963                 lb[lb.length] = ct.apply(p);
47964                 lsb[lsb.length] = st.apply(p);
47965             }
47966         }
47967         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47968                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47969     },
47970
47971     updateHeaders : function(){
47972         var html = this.renderHeaders();
47973         this.lockedHd.update(html[0]);
47974         this.mainHd.update(html[1]);
47975     },
47976
47977     /**
47978      * Focuses the specified row.
47979      * @param {Number} row The row index
47980      */
47981     focusRow : function(row)
47982     {
47983         //Roo.log('GridView.focusRow');
47984         var x = this.scroller.dom.scrollLeft;
47985         this.focusCell(row, 0, false);
47986         this.scroller.dom.scrollLeft = x;
47987     },
47988
47989     /**
47990      * Focuses the specified cell.
47991      * @param {Number} row The row index
47992      * @param {Number} col The column index
47993      * @param {Boolean} hscroll false to disable horizontal scrolling
47994      */
47995     focusCell : function(row, col, hscroll)
47996     {
47997         //Roo.log('GridView.focusCell');
47998         var el = this.ensureVisible(row, col, hscroll);
47999         this.focusEl.alignTo(el, "tl-tl");
48000         if(Roo.isGecko){
48001             this.focusEl.focus();
48002         }else{
48003             this.focusEl.focus.defer(1, this.focusEl);
48004         }
48005     },
48006
48007     /**
48008      * Scrolls the specified cell into view
48009      * @param {Number} row The row index
48010      * @param {Number} col The column index
48011      * @param {Boolean} hscroll false to disable horizontal scrolling
48012      */
48013     ensureVisible : function(row, col, hscroll)
48014     {
48015         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48016         //return null; //disable for testing.
48017         if(typeof row != "number"){
48018             row = row.rowIndex;
48019         }
48020         if(row < 0 && row >= this.ds.getCount()){
48021             return  null;
48022         }
48023         col = (col !== undefined ? col : 0);
48024         var cm = this.grid.colModel;
48025         while(cm.isHidden(col)){
48026             col++;
48027         }
48028
48029         var el = this.getCell(row, col);
48030         if(!el){
48031             return null;
48032         }
48033         var c = this.scroller.dom;
48034
48035         var ctop = parseInt(el.offsetTop, 10);
48036         var cleft = parseInt(el.offsetLeft, 10);
48037         var cbot = ctop + el.offsetHeight;
48038         var cright = cleft + el.offsetWidth;
48039         
48040         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48041         var stop = parseInt(c.scrollTop, 10);
48042         var sleft = parseInt(c.scrollLeft, 10);
48043         var sbot = stop + ch;
48044         var sright = sleft + c.clientWidth;
48045         /*
48046         Roo.log('GridView.ensureVisible:' +
48047                 ' ctop:' + ctop +
48048                 ' c.clientHeight:' + c.clientHeight +
48049                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48050                 ' stop:' + stop +
48051                 ' cbot:' + cbot +
48052                 ' sbot:' + sbot +
48053                 ' ch:' + ch  
48054                 );
48055         */
48056         if(ctop < stop){
48057              c.scrollTop = ctop;
48058             //Roo.log("set scrolltop to ctop DISABLE?");
48059         }else if(cbot > sbot){
48060             //Roo.log("set scrolltop to cbot-ch");
48061             c.scrollTop = cbot-ch;
48062         }
48063         
48064         if(hscroll !== false){
48065             if(cleft < sleft){
48066                 c.scrollLeft = cleft;
48067             }else if(cright > sright){
48068                 c.scrollLeft = cright-c.clientWidth;
48069             }
48070         }
48071          
48072         return el;
48073     },
48074
48075     updateColumns : function(){
48076         this.grid.stopEditing();
48077         var cm = this.grid.colModel, colIds = this.getColumnIds();
48078         //var totalWidth = cm.getTotalWidth();
48079         var pos = 0;
48080         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48081             //if(cm.isHidden(i)) continue;
48082             var w = cm.getColumnWidth(i);
48083             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48084             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48085         }
48086         this.updateSplitters();
48087     },
48088
48089     generateRules : function(cm){
48090         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48091         Roo.util.CSS.removeStyleSheet(rulesId);
48092         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48093             var cid = cm.getColumnId(i);
48094             var align = '';
48095             if(cm.config[i].align){
48096                 align = 'text-align:'+cm.config[i].align+';';
48097             }
48098             var hidden = '';
48099             if(cm.isHidden(i)){
48100                 hidden = 'display:none;';
48101             }
48102             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48103             ruleBuf.push(
48104                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48105                     this.hdSelector, cid, " {\n", align, width, "}\n",
48106                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48107                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48108         }
48109         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48110     },
48111
48112     updateSplitters : function(){
48113         var cm = this.cm, s = this.getSplitters();
48114         if(s){ // splitters not created yet
48115             var pos = 0, locked = true;
48116             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48117                 if(cm.isHidden(i)) continue;
48118                 var w = cm.getColumnWidth(i); // make sure it's a number
48119                 if(!cm.isLocked(i) && locked){
48120                     pos = 0;
48121                     locked = false;
48122                 }
48123                 pos += w;
48124                 s[i].style.left = (pos-this.splitOffset) + "px";
48125             }
48126         }
48127     },
48128
48129     handleHiddenChange : function(colModel, colIndex, hidden){
48130         if(hidden){
48131             this.hideColumn(colIndex);
48132         }else{
48133             this.unhideColumn(colIndex);
48134         }
48135     },
48136
48137     hideColumn : function(colIndex){
48138         var cid = this.getColumnId(colIndex);
48139         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48140         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48141         if(Roo.isSafari){
48142             this.updateHeaders();
48143         }
48144         this.updateSplitters();
48145         this.layout();
48146     },
48147
48148     unhideColumn : function(colIndex){
48149         var cid = this.getColumnId(colIndex);
48150         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48151         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48152
48153         if(Roo.isSafari){
48154             this.updateHeaders();
48155         }
48156         this.updateSplitters();
48157         this.layout();
48158     },
48159
48160     insertRows : function(dm, firstRow, lastRow, isUpdate){
48161         if(firstRow == 0 && lastRow == dm.getCount()-1){
48162             this.refresh();
48163         }else{
48164             if(!isUpdate){
48165                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48166             }
48167             var s = this.getScrollState();
48168             var markup = this.renderRows(firstRow, lastRow);
48169             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48170             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48171             this.restoreScroll(s);
48172             if(!isUpdate){
48173                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48174                 this.syncRowHeights(firstRow, lastRow);
48175                 this.stripeRows(firstRow);
48176                 this.layout();
48177             }
48178         }
48179     },
48180
48181     bufferRows : function(markup, target, index){
48182         var before = null, trows = target.rows, tbody = target.tBodies[0];
48183         if(index < trows.length){
48184             before = trows[index];
48185         }
48186         var b = document.createElement("div");
48187         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
48188         var rows = b.firstChild.rows;
48189         for(var i = 0, len = rows.length; i < len; i++){
48190             if(before){
48191                 tbody.insertBefore(rows[0], before);
48192             }else{
48193                 tbody.appendChild(rows[0]);
48194             }
48195         }
48196         b.innerHTML = "";
48197         b = null;
48198     },
48199
48200     deleteRows : function(dm, firstRow, lastRow){
48201         if(dm.getRowCount()<1){
48202             this.fireEvent("beforerefresh", this);
48203             this.mainBody.update("");
48204             this.lockedBody.update("");
48205             this.fireEvent("refresh", this);
48206         }else{
48207             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
48208             var bt = this.getBodyTable();
48209             var tbody = bt.firstChild;
48210             var rows = bt.rows;
48211             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
48212                 tbody.removeChild(rows[firstRow]);
48213             }
48214             this.stripeRows(firstRow);
48215             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
48216         }
48217     },
48218
48219     updateRows : function(dataSource, firstRow, lastRow){
48220         var s = this.getScrollState();
48221         this.refresh();
48222         this.restoreScroll(s);
48223     },
48224
48225     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
48226         if(!noRefresh){
48227            this.refresh();
48228         }
48229         this.updateHeaderSortState();
48230     },
48231
48232     getScrollState : function(){
48233         
48234         var sb = this.scroller.dom;
48235         return {left: sb.scrollLeft, top: sb.scrollTop};
48236     },
48237
48238     stripeRows : function(startRow){
48239         if(!this.grid.stripeRows || this.ds.getCount() < 1){
48240             return;
48241         }
48242         startRow = startRow || 0;
48243         var rows = this.getBodyTable().rows;
48244         var lrows = this.getLockedTable().rows;
48245         var cls = ' x-grid-row-alt ';
48246         for(var i = startRow, len = rows.length; i < len; i++){
48247             var row = rows[i], lrow = lrows[i];
48248             var isAlt = ((i+1) % 2 == 0);
48249             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
48250             if(isAlt == hasAlt){
48251                 continue;
48252             }
48253             if(isAlt){
48254                 row.className += " x-grid-row-alt";
48255             }else{
48256                 row.className = row.className.replace("x-grid-row-alt", "");
48257             }
48258             if(lrow){
48259                 lrow.className = row.className;
48260             }
48261         }
48262     },
48263
48264     restoreScroll : function(state){
48265         //Roo.log('GridView.restoreScroll');
48266         var sb = this.scroller.dom;
48267         sb.scrollLeft = state.left;
48268         sb.scrollTop = state.top;
48269         this.syncScroll();
48270     },
48271
48272     syncScroll : function(){
48273         //Roo.log('GridView.syncScroll');
48274         var sb = this.scroller.dom;
48275         var sh = this.mainHd.dom;
48276         var bs = this.mainBody.dom;
48277         var lv = this.lockedBody.dom;
48278         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
48279         lv.scrollTop = bs.scrollTop = sb.scrollTop;
48280     },
48281
48282     handleScroll : function(e){
48283         this.syncScroll();
48284         var sb = this.scroller.dom;
48285         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
48286         e.stopEvent();
48287     },
48288
48289     handleWheel : function(e){
48290         var d = e.getWheelDelta();
48291         this.scroller.dom.scrollTop -= d*22;
48292         // set this here to prevent jumpy scrolling on large tables
48293         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
48294         e.stopEvent();
48295     },
48296
48297     renderRows : function(startRow, endRow){
48298         // pull in all the crap needed to render rows
48299         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
48300         var colCount = cm.getColumnCount();
48301
48302         if(ds.getCount() < 1){
48303             return ["", ""];
48304         }
48305
48306         // build a map for all the columns
48307         var cs = [];
48308         for(var i = 0; i < colCount; i++){
48309             var name = cm.getDataIndex(i);
48310             cs[i] = {
48311                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
48312                 renderer : cm.getRenderer(i),
48313                 id : cm.getColumnId(i),
48314                 locked : cm.isLocked(i)
48315             };
48316         }
48317
48318         startRow = startRow || 0;
48319         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
48320
48321         // records to render
48322         var rs = ds.getRange(startRow, endRow);
48323
48324         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
48325     },
48326
48327     // As much as I hate to duplicate code, this was branched because FireFox really hates
48328     // [].join("") on strings. The performance difference was substantial enough to
48329     // branch this function
48330     doRender : Roo.isGecko ?
48331             function(cs, rs, ds, startRow, colCount, stripe){
48332                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48333                 // buffers
48334                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48335                 
48336                 var hasListener = this.grid.hasListener('rowclass');
48337                 var rowcfg = {};
48338                 for(var j = 0, len = rs.length; j < len; j++){
48339                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
48340                     for(var i = 0; i < colCount; i++){
48341                         c = cs[i];
48342                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48343                         p.id = c.id;
48344                         p.css = p.attr = "";
48345                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48346                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48347                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48348                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48349                         }
48350                         var markup = ct.apply(p);
48351                         if(!c.locked){
48352                             cb+= markup;
48353                         }else{
48354                             lcb+= markup;
48355                         }
48356                     }
48357                     var alt = [];
48358                     if(stripe && ((rowIndex+1) % 2 == 0)){
48359                         alt.push("x-grid-row-alt")
48360                     }
48361                     if(r.dirty){
48362                         alt.push(  " x-grid-dirty-row");
48363                     }
48364                     rp.cells = lcb;
48365                     if(this.getRowClass){
48366                         alt.push(this.getRowClass(r, rowIndex));
48367                     }
48368                     if (hasListener) {
48369                         rowcfg = {
48370                              
48371                             record: r,
48372                             rowIndex : rowIndex,
48373                             rowClass : ''
48374                         }
48375                         this.grid.fireEvent('rowclass', this, rowcfg);
48376                         alt.push(rowcfg.rowClass);
48377                     }
48378                     rp.alt = alt.join(" ");
48379                     lbuf+= rt.apply(rp);
48380                     rp.cells = cb;
48381                     buf+=  rt.apply(rp);
48382                 }
48383                 return [lbuf, buf];
48384             } :
48385             function(cs, rs, ds, startRow, colCount, stripe){
48386                 var ts = this.templates, ct = ts.cell, rt = ts.row;
48387                 // buffers
48388                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
48389                 var hasListener = this.grid.hasListener('rowclass');
48390                 var rowcfg = {};
48391                 for(var j = 0, len = rs.length; j < len; j++){
48392                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
48393                     for(var i = 0; i < colCount; i++){
48394                         c = cs[i];
48395                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
48396                         p.id = c.id;
48397                         p.css = p.attr = "";
48398                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
48399                         if(p.value == undefined || p.value === "") p.value = "&#160;";
48400                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
48401                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
48402                         }
48403                         var markup = ct.apply(p);
48404                         if(!c.locked){
48405                             cb[cb.length] = markup;
48406                         }else{
48407                             lcb[lcb.length] = markup;
48408                         }
48409                     }
48410                     var alt = [];
48411                     if(stripe && ((rowIndex+1) % 2 == 0)){
48412                         alt.push( "x-grid-row-alt");
48413                     }
48414                     if(r.dirty){
48415                         alt.push(" x-grid-dirty-row");
48416                     }
48417                     rp.cells = lcb;
48418                     if(this.getRowClass){
48419                         alt.push( this.getRowClass(r, rowIndex));
48420                     }
48421                     if (hasListener) {
48422                         rowcfg = {
48423                              
48424                             record: r,
48425                             rowIndex : rowIndex,
48426                             rowClass : ''
48427                         }
48428                         this.grid.fireEvent('rowclass', this, rowcfg);
48429                         alt.push(rowcfg.rowClass);
48430                     }
48431                     rp.alt = alt.join(" ");
48432                     rp.cells = lcb.join("");
48433                     lbuf[lbuf.length] = rt.apply(rp);
48434                     rp.cells = cb.join("");
48435                     buf[buf.length] =  rt.apply(rp);
48436                 }
48437                 return [lbuf.join(""), buf.join("")];
48438             },
48439
48440     renderBody : function(){
48441         var markup = this.renderRows();
48442         var bt = this.templates.body;
48443         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48444     },
48445
48446     /**
48447      * Refreshes the grid
48448      * @param {Boolean} headersToo
48449      */
48450     refresh : function(headersToo){
48451         this.fireEvent("beforerefresh", this);
48452         this.grid.stopEditing();
48453         var result = this.renderBody();
48454         this.lockedBody.update(result[0]);
48455         this.mainBody.update(result[1]);
48456         if(headersToo === true){
48457             this.updateHeaders();
48458             this.updateColumns();
48459             this.updateSplitters();
48460             this.updateHeaderSortState();
48461         }
48462         this.syncRowHeights();
48463         this.layout();
48464         this.fireEvent("refresh", this);
48465     },
48466
48467     handleColumnMove : function(cm, oldIndex, newIndex){
48468         this.indexMap = null;
48469         var s = this.getScrollState();
48470         this.refresh(true);
48471         this.restoreScroll(s);
48472         this.afterMove(newIndex);
48473     },
48474
48475     afterMove : function(colIndex){
48476         if(this.enableMoveAnim && Roo.enableFx){
48477             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48478         }
48479         // if multisort - fix sortOrder, and reload..
48480         if (this.grid.dataSource.multiSort) {
48481             // the we can call sort again..
48482             var dm = this.grid.dataSource;
48483             var cm = this.grid.colModel;
48484             var so = [];
48485             for(var i = 0; i < cm.config.length; i++ ) {
48486                 
48487                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
48488                     continue; // dont' bother, it's not in sort list or being set.
48489                 }
48490                 
48491                 so.push(cm.config[i].dataIndex);
48492             };
48493             dm.sortOrder = so;
48494             dm.load(dm.lastOptions);
48495             
48496             
48497         }
48498         
48499     },
48500
48501     updateCell : function(dm, rowIndex, dataIndex){
48502         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48503         if(typeof colIndex == "undefined"){ // not present in grid
48504             return;
48505         }
48506         var cm = this.grid.colModel;
48507         var cell = this.getCell(rowIndex, colIndex);
48508         var cellText = this.getCellText(rowIndex, colIndex);
48509
48510         var p = {
48511             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48512             id : cm.getColumnId(colIndex),
48513             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48514         };
48515         var renderer = cm.getRenderer(colIndex);
48516         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48517         if(typeof val == "undefined" || val === "") val = "&#160;";
48518         cellText.innerHTML = val;
48519         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48520         this.syncRowHeights(rowIndex, rowIndex);
48521     },
48522
48523     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48524         var maxWidth = 0;
48525         if(this.grid.autoSizeHeaders){
48526             var h = this.getHeaderCellMeasure(colIndex);
48527             maxWidth = Math.max(maxWidth, h.scrollWidth);
48528         }
48529         var tb, index;
48530         if(this.cm.isLocked(colIndex)){
48531             tb = this.getLockedTable();
48532             index = colIndex;
48533         }else{
48534             tb = this.getBodyTable();
48535             index = colIndex - this.cm.getLockedCount();
48536         }
48537         if(tb && tb.rows){
48538             var rows = tb.rows;
48539             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48540             for(var i = 0; i < stopIndex; i++){
48541                 var cell = rows[i].childNodes[index].firstChild;
48542                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48543             }
48544         }
48545         return maxWidth + /*margin for error in IE*/ 5;
48546     },
48547     /**
48548      * Autofit a column to its content.
48549      * @param {Number} colIndex
48550      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48551      */
48552      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48553          if(this.cm.isHidden(colIndex)){
48554              return; // can't calc a hidden column
48555          }
48556         if(forceMinSize){
48557             var cid = this.cm.getColumnId(colIndex);
48558             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48559            if(this.grid.autoSizeHeaders){
48560                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48561            }
48562         }
48563         var newWidth = this.calcColumnWidth(colIndex);
48564         this.cm.setColumnWidth(colIndex,
48565             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48566         if(!suppressEvent){
48567             this.grid.fireEvent("columnresize", colIndex, newWidth);
48568         }
48569     },
48570
48571     /**
48572      * Autofits all columns to their content and then expands to fit any extra space in the grid
48573      */
48574      autoSizeColumns : function(){
48575         var cm = this.grid.colModel;
48576         var colCount = cm.getColumnCount();
48577         for(var i = 0; i < colCount; i++){
48578             this.autoSizeColumn(i, true, true);
48579         }
48580         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48581             this.fitColumns();
48582         }else{
48583             this.updateColumns();
48584             this.layout();
48585         }
48586     },
48587
48588     /**
48589      * Autofits all columns to the grid's width proportionate with their current size
48590      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48591      */
48592     fitColumns : function(reserveScrollSpace){
48593         var cm = this.grid.colModel;
48594         var colCount = cm.getColumnCount();
48595         var cols = [];
48596         var width = 0;
48597         var i, w;
48598         for (i = 0; i < colCount; i++){
48599             if(!cm.isHidden(i) && !cm.isFixed(i)){
48600                 w = cm.getColumnWidth(i);
48601                 cols.push(i);
48602                 cols.push(w);
48603                 width += w;
48604             }
48605         }
48606         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48607         if(reserveScrollSpace){
48608             avail -= 17;
48609         }
48610         var frac = (avail - cm.getTotalWidth())/width;
48611         while (cols.length){
48612             w = cols.pop();
48613             i = cols.pop();
48614             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48615         }
48616         this.updateColumns();
48617         this.layout();
48618     },
48619
48620     onRowSelect : function(rowIndex){
48621         var row = this.getRowComposite(rowIndex);
48622         row.addClass("x-grid-row-selected");
48623     },
48624
48625     onRowDeselect : function(rowIndex){
48626         var row = this.getRowComposite(rowIndex);
48627         row.removeClass("x-grid-row-selected");
48628     },
48629
48630     onCellSelect : function(row, col){
48631         var cell = this.getCell(row, col);
48632         if(cell){
48633             Roo.fly(cell).addClass("x-grid-cell-selected");
48634         }
48635     },
48636
48637     onCellDeselect : function(row, col){
48638         var cell = this.getCell(row, col);
48639         if(cell){
48640             Roo.fly(cell).removeClass("x-grid-cell-selected");
48641         }
48642     },
48643
48644     updateHeaderSortState : function(){
48645         
48646         // sort state can be single { field: xxx, direction : yyy}
48647         // or   { xxx=>ASC , yyy : DESC ..... }
48648         
48649         var mstate = {};
48650         if (!this.ds.multiSort) { 
48651             var state = this.ds.getSortState();
48652             if(!state){
48653                 return;
48654             }
48655             mstate[state.field] = state.direction;
48656             // FIXME... - this is not used here.. but might be elsewhere..
48657             this.sortState = state;
48658             
48659         } else {
48660             mstate = this.ds.sortToggle;
48661         }
48662         //remove existing sort classes..
48663         
48664         var sc = this.sortClasses;
48665         var hds = this.el.select(this.headerSelector).removeClass(sc);
48666         
48667         for(var f in mstate) {
48668         
48669             var sortColumn = this.cm.findColumnIndex(f);
48670             
48671             if(sortColumn != -1){
48672                 var sortDir = mstate[f];        
48673                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48674             }
48675         }
48676         
48677          
48678         
48679     },
48680
48681
48682     handleHeaderClick : function(g, index){
48683         if(this.headersDisabled){
48684             return;
48685         }
48686         var dm = g.dataSource, cm = g.colModel;
48687         if(!cm.isSortable(index)){
48688             return;
48689         }
48690         g.stopEditing();
48691         
48692         if (dm.multiSort) {
48693             // update the sortOrder
48694             var so = [];
48695             for(var i = 0; i < cm.config.length; i++ ) {
48696                 
48697                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
48698                     continue; // dont' bother, it's not in sort list or being set.
48699                 }
48700                 
48701                 so.push(cm.config[i].dataIndex);
48702             };
48703             dm.sortOrder = so;
48704         }
48705         
48706         
48707         dm.sort(cm.getDataIndex(index));
48708     },
48709
48710
48711     destroy : function(){
48712         if(this.colMenu){
48713             this.colMenu.removeAll();
48714             Roo.menu.MenuMgr.unregister(this.colMenu);
48715             this.colMenu.getEl().remove();
48716             delete this.colMenu;
48717         }
48718         if(this.hmenu){
48719             this.hmenu.removeAll();
48720             Roo.menu.MenuMgr.unregister(this.hmenu);
48721             this.hmenu.getEl().remove();
48722             delete this.hmenu;
48723         }
48724         if(this.grid.enableColumnMove){
48725             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48726             if(dds){
48727                 for(var dd in dds){
48728                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48729                         var elid = dds[dd].dragElId;
48730                         dds[dd].unreg();
48731                         Roo.get(elid).remove();
48732                     } else if(dds[dd].config.isTarget){
48733                         dds[dd].proxyTop.remove();
48734                         dds[dd].proxyBottom.remove();
48735                         dds[dd].unreg();
48736                     }
48737                     if(Roo.dd.DDM.locationCache[dd]){
48738                         delete Roo.dd.DDM.locationCache[dd];
48739                     }
48740                 }
48741                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48742             }
48743         }
48744         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48745         this.bind(null, null);
48746         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48747     },
48748
48749     handleLockChange : function(){
48750         this.refresh(true);
48751     },
48752
48753     onDenyColumnLock : function(){
48754
48755     },
48756
48757     onDenyColumnHide : function(){
48758
48759     },
48760
48761     handleHdMenuClick : function(item){
48762         var index = this.hdCtxIndex;
48763         var cm = this.cm, ds = this.ds;
48764         switch(item.id){
48765             case "asc":
48766                 ds.sort(cm.getDataIndex(index), "ASC");
48767                 break;
48768             case "desc":
48769                 ds.sort(cm.getDataIndex(index), "DESC");
48770                 break;
48771             case "lock":
48772                 var lc = cm.getLockedCount();
48773                 if(cm.getColumnCount(true) <= lc+1){
48774                     this.onDenyColumnLock();
48775                     return;
48776                 }
48777                 if(lc != index){
48778                     cm.setLocked(index, true, true);
48779                     cm.moveColumn(index, lc);
48780                     this.grid.fireEvent("columnmove", index, lc);
48781                 }else{
48782                     cm.setLocked(index, true);
48783                 }
48784             break;
48785             case "unlock":
48786                 var lc = cm.getLockedCount();
48787                 if((lc-1) != index){
48788                     cm.setLocked(index, false, true);
48789                     cm.moveColumn(index, lc-1);
48790                     this.grid.fireEvent("columnmove", index, lc-1);
48791                 }else{
48792                     cm.setLocked(index, false);
48793                 }
48794             break;
48795             default:
48796                 index = cm.getIndexById(item.id.substr(4));
48797                 if(index != -1){
48798                     if(item.checked && cm.getColumnCount(true) <= 1){
48799                         this.onDenyColumnHide();
48800                         return false;
48801                     }
48802                     cm.setHidden(index, item.checked);
48803                 }
48804         }
48805         return true;
48806     },
48807
48808     beforeColMenuShow : function(){
48809         var cm = this.cm,  colCount = cm.getColumnCount();
48810         this.colMenu.removeAll();
48811         for(var i = 0; i < colCount; i++){
48812             this.colMenu.add(new Roo.menu.CheckItem({
48813                 id: "col-"+cm.getColumnId(i),
48814                 text: cm.getColumnHeader(i),
48815                 checked: !cm.isHidden(i),
48816                 hideOnClick:false
48817             }));
48818         }
48819     },
48820
48821     handleHdCtx : function(g, index, e){
48822         e.stopEvent();
48823         var hd = this.getHeaderCell(index);
48824         this.hdCtxIndex = index;
48825         var ms = this.hmenu.items, cm = this.cm;
48826         ms.get("asc").setDisabled(!cm.isSortable(index));
48827         ms.get("desc").setDisabled(!cm.isSortable(index));
48828         if(this.grid.enableColLock !== false){
48829             ms.get("lock").setDisabled(cm.isLocked(index));
48830             ms.get("unlock").setDisabled(!cm.isLocked(index));
48831         }
48832         this.hmenu.show(hd, "tl-bl");
48833     },
48834
48835     handleHdOver : function(e){
48836         var hd = this.findHeaderCell(e.getTarget());
48837         if(hd && !this.headersDisabled){
48838             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48839                this.fly(hd).addClass("x-grid-hd-over");
48840             }
48841         }
48842     },
48843
48844     handleHdOut : function(e){
48845         var hd = this.findHeaderCell(e.getTarget());
48846         if(hd){
48847             this.fly(hd).removeClass("x-grid-hd-over");
48848         }
48849     },
48850
48851     handleSplitDblClick : function(e, t){
48852         var i = this.getCellIndex(t);
48853         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48854             this.autoSizeColumn(i, true);
48855             this.layout();
48856         }
48857     },
48858
48859     render : function(){
48860
48861         var cm = this.cm;
48862         var colCount = cm.getColumnCount();
48863
48864         if(this.grid.monitorWindowResize === true){
48865             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48866         }
48867         var header = this.renderHeaders();
48868         var body = this.templates.body.apply({rows:""});
48869         var html = this.templates.master.apply({
48870             lockedBody: body,
48871             body: body,
48872             lockedHeader: header[0],
48873             header: header[1]
48874         });
48875
48876         //this.updateColumns();
48877
48878         this.grid.getGridEl().dom.innerHTML = html;
48879
48880         this.initElements();
48881         
48882         // a kludge to fix the random scolling effect in webkit
48883         this.el.on("scroll", function() {
48884             this.el.dom.scrollTop=0; // hopefully not recursive..
48885         },this);
48886
48887         this.scroller.on("scroll", this.handleScroll, this);
48888         this.lockedBody.on("mousewheel", this.handleWheel, this);
48889         this.mainBody.on("mousewheel", this.handleWheel, this);
48890
48891         this.mainHd.on("mouseover", this.handleHdOver, this);
48892         this.mainHd.on("mouseout", this.handleHdOut, this);
48893         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48894                 {delegate: "."+this.splitClass});
48895
48896         this.lockedHd.on("mouseover", this.handleHdOver, this);
48897         this.lockedHd.on("mouseout", this.handleHdOut, this);
48898         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48899                 {delegate: "."+this.splitClass});
48900
48901         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48902             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48903         }
48904
48905         this.updateSplitters();
48906
48907         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48908             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48909             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48910         }
48911
48912         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48913             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48914             this.hmenu.add(
48915                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48916                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48917             );
48918             if(this.grid.enableColLock !== false){
48919                 this.hmenu.add('-',
48920                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48921                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48922                 );
48923             }
48924             if(this.grid.enableColumnHide !== false){
48925
48926                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48927                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48928                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48929
48930                 this.hmenu.add('-',
48931                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48932                 );
48933             }
48934             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48935
48936             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48937         }
48938
48939         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48940             this.dd = new Roo.grid.GridDragZone(this.grid, {
48941                 ddGroup : this.grid.ddGroup || 'GridDD'
48942             });
48943         }
48944
48945         /*
48946         for(var i = 0; i < colCount; i++){
48947             if(cm.isHidden(i)){
48948                 this.hideColumn(i);
48949             }
48950             if(cm.config[i].align){
48951                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48952                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48953             }
48954         }*/
48955         
48956         this.updateHeaderSortState();
48957
48958         this.beforeInitialResize();
48959         this.layout(true);
48960
48961         // two part rendering gives faster view to the user
48962         this.renderPhase2.defer(1, this);
48963     },
48964
48965     renderPhase2 : function(){
48966         // render the rows now
48967         this.refresh();
48968         if(this.grid.autoSizeColumns){
48969             this.autoSizeColumns();
48970         }
48971     },
48972
48973     beforeInitialResize : function(){
48974
48975     },
48976
48977     onColumnSplitterMoved : function(i, w){
48978         this.userResized = true;
48979         var cm = this.grid.colModel;
48980         cm.setColumnWidth(i, w, true);
48981         var cid = cm.getColumnId(i);
48982         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48983         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48984         this.updateSplitters();
48985         this.layout();
48986         this.grid.fireEvent("columnresize", i, w);
48987     },
48988
48989     syncRowHeights : function(startIndex, endIndex){
48990         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48991             startIndex = startIndex || 0;
48992             var mrows = this.getBodyTable().rows;
48993             var lrows = this.getLockedTable().rows;
48994             var len = mrows.length-1;
48995             endIndex = Math.min(endIndex || len, len);
48996             for(var i = startIndex; i <= endIndex; i++){
48997                 var m = mrows[i], l = lrows[i];
48998                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48999                 m.style.height = l.style.height = h + "px";
49000             }
49001         }
49002     },
49003
49004     layout : function(initialRender, is2ndPass){
49005         var g = this.grid;
49006         var auto = g.autoHeight;
49007         var scrollOffset = 16;
49008         var c = g.getGridEl(), cm = this.cm,
49009                 expandCol = g.autoExpandColumn,
49010                 gv = this;
49011         //c.beginMeasure();
49012
49013         if(!c.dom.offsetWidth){ // display:none?
49014             if(initialRender){
49015                 this.lockedWrap.show();
49016                 this.mainWrap.show();
49017             }
49018             return;
49019         }
49020
49021         var hasLock = this.cm.isLocked(0);
49022
49023         var tbh = this.headerPanel.getHeight();
49024         var bbh = this.footerPanel.getHeight();
49025
49026         if(auto){
49027             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49028             var newHeight = ch + c.getBorderWidth("tb");
49029             if(g.maxHeight){
49030                 newHeight = Math.min(g.maxHeight, newHeight);
49031             }
49032             c.setHeight(newHeight);
49033         }
49034
49035         if(g.autoWidth){
49036             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49037         }
49038
49039         var s = this.scroller;
49040
49041         var csize = c.getSize(true);
49042
49043         this.el.setSize(csize.width, csize.height);
49044
49045         this.headerPanel.setWidth(csize.width);
49046         this.footerPanel.setWidth(csize.width);
49047
49048         var hdHeight = this.mainHd.getHeight();
49049         var vw = csize.width;
49050         var vh = csize.height - (tbh + bbh);
49051
49052         s.setSize(vw, vh);
49053
49054         var bt = this.getBodyTable();
49055         var ltWidth = hasLock ?
49056                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49057
49058         var scrollHeight = bt.offsetHeight;
49059         var scrollWidth = ltWidth + bt.offsetWidth;
49060         var vscroll = false, hscroll = false;
49061
49062         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49063
49064         var lw = this.lockedWrap, mw = this.mainWrap;
49065         var lb = this.lockedBody, mb = this.mainBody;
49066
49067         setTimeout(function(){
49068             var t = s.dom.offsetTop;
49069             var w = s.dom.clientWidth,
49070                 h = s.dom.clientHeight;
49071
49072             lw.setTop(t);
49073             lw.setSize(ltWidth, h);
49074
49075             mw.setLeftTop(ltWidth, t);
49076             mw.setSize(w-ltWidth, h);
49077
49078             lb.setHeight(h-hdHeight);
49079             mb.setHeight(h-hdHeight);
49080
49081             if(is2ndPass !== true && !gv.userResized && expandCol){
49082                 // high speed resize without full column calculation
49083                 
49084                 var ci = cm.getIndexById(expandCol);
49085                 if (ci < 0) {
49086                     ci = cm.findColumnIndex(expandCol);
49087                 }
49088                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49089                 var expandId = cm.getColumnId(ci);
49090                 var  tw = cm.getTotalWidth(false);
49091                 var currentWidth = cm.getColumnWidth(ci);
49092                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49093                 if(currentWidth != cw){
49094                     cm.setColumnWidth(ci, cw, true);
49095                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49096                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49097                     gv.updateSplitters();
49098                     gv.layout(false, true);
49099                 }
49100             }
49101
49102             if(initialRender){
49103                 lw.show();
49104                 mw.show();
49105             }
49106             //c.endMeasure();
49107         }, 10);
49108     },
49109
49110     onWindowResize : function(){
49111         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49112             return;
49113         }
49114         this.layout();
49115     },
49116
49117     appendFooter : function(parentEl){
49118         return null;
49119     },
49120
49121     sortAscText : "Sort Ascending",
49122     sortDescText : "Sort Descending",
49123     lockText : "Lock Column",
49124     unlockText : "Unlock Column",
49125     columnsText : "Columns"
49126 });
49127
49128
49129 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49130     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49131     this.proxy.el.addClass('x-grid3-col-dd');
49132 };
49133
49134 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49135     handleMouseDown : function(e){
49136
49137     },
49138
49139     callHandleMouseDown : function(e){
49140         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49141     }
49142 });
49143 /*
49144  * Based on:
49145  * Ext JS Library 1.1.1
49146  * Copyright(c) 2006-2007, Ext JS, LLC.
49147  *
49148  * Originally Released Under LGPL - original licence link has changed is not relivant.
49149  *
49150  * Fork - LGPL
49151  * <script type="text/javascript">
49152  */
49153  
49154 // private
49155 // This is a support class used internally by the Grid components
49156 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49157     this.grid = grid;
49158     this.view = grid.getView();
49159     this.proxy = this.view.resizeProxy;
49160     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49161         "gridSplitters" + this.grid.getGridEl().id, {
49162         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49163     });
49164     this.setHandleElId(Roo.id(hd));
49165     this.setOuterHandleElId(Roo.id(hd2));
49166     this.scroll = false;
49167 };
49168 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49169     fly: Roo.Element.fly,
49170
49171     b4StartDrag : function(x, y){
49172         this.view.headersDisabled = true;
49173         this.proxy.setHeight(this.view.mainWrap.getHeight());
49174         var w = this.cm.getColumnWidth(this.cellIndex);
49175         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49176         this.resetConstraints();
49177         this.setXConstraint(minw, 1000);
49178         this.setYConstraint(0, 0);
49179         this.minX = x - minw;
49180         this.maxX = x + 1000;
49181         this.startPos = x;
49182         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49183     },
49184
49185
49186     handleMouseDown : function(e){
49187         ev = Roo.EventObject.setEvent(e);
49188         var t = this.fly(ev.getTarget());
49189         if(t.hasClass("x-grid-split")){
49190             this.cellIndex = this.view.getCellIndex(t.dom);
49191             this.split = t.dom;
49192             this.cm = this.grid.colModel;
49193             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
49194                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
49195             }
49196         }
49197     },
49198
49199     endDrag : function(e){
49200         this.view.headersDisabled = false;
49201         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
49202         var diff = endX - this.startPos;
49203         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
49204     },
49205
49206     autoOffset : function(){
49207         this.setDelta(0,0);
49208     }
49209 });/*
49210  * Based on:
49211  * Ext JS Library 1.1.1
49212  * Copyright(c) 2006-2007, Ext JS, LLC.
49213  *
49214  * Originally Released Under LGPL - original licence link has changed is not relivant.
49215  *
49216  * Fork - LGPL
49217  * <script type="text/javascript">
49218  */
49219  
49220 // private
49221 // This is a support class used internally by the Grid components
49222 Roo.grid.GridDragZone = function(grid, config){
49223     this.view = grid.getView();
49224     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
49225     if(this.view.lockedBody){
49226         this.setHandleElId(Roo.id(this.view.mainBody.dom));
49227         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
49228     }
49229     this.scroll = false;
49230     this.grid = grid;
49231     this.ddel = document.createElement('div');
49232     this.ddel.className = 'x-grid-dd-wrap';
49233 };
49234
49235 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
49236     ddGroup : "GridDD",
49237
49238     getDragData : function(e){
49239         var t = Roo.lib.Event.getTarget(e);
49240         var rowIndex = this.view.findRowIndex(t);
49241         if(rowIndex !== false){
49242             var sm = this.grid.selModel;
49243             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
49244               //  sm.mouseDown(e, t);
49245             //}
49246             if (e.hasModifier()){
49247                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
49248             }
49249             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
49250         }
49251         return false;
49252     },
49253
49254     onInitDrag : function(e){
49255         var data = this.dragData;
49256         this.ddel.innerHTML = this.grid.getDragDropText();
49257         this.proxy.update(this.ddel);
49258         // fire start drag?
49259     },
49260
49261     afterRepair : function(){
49262         this.dragging = false;
49263     },
49264
49265     getRepairXY : function(e, data){
49266         return false;
49267     },
49268
49269     onEndDrag : function(data, e){
49270         // fire end drag?
49271     },
49272
49273     onValidDrop : function(dd, e, id){
49274         // fire drag drop?
49275         this.hideProxy();
49276     },
49277
49278     beforeInvalidDrop : function(e, id){
49279
49280     }
49281 });/*
49282  * Based on:
49283  * Ext JS Library 1.1.1
49284  * Copyright(c) 2006-2007, Ext JS, LLC.
49285  *
49286  * Originally Released Under LGPL - original licence link has changed is not relivant.
49287  *
49288  * Fork - LGPL
49289  * <script type="text/javascript">
49290  */
49291  
49292
49293 /**
49294  * @class Roo.grid.ColumnModel
49295  * @extends Roo.util.Observable
49296  * This is the default implementation of a ColumnModel used by the Grid. It defines
49297  * the columns in the grid.
49298  * <br>Usage:<br>
49299  <pre><code>
49300  var colModel = new Roo.grid.ColumnModel([
49301         {header: "Ticker", width: 60, sortable: true, locked: true},
49302         {header: "Company Name", width: 150, sortable: true},
49303         {header: "Market Cap.", width: 100, sortable: true},
49304         {header: "$ Sales", width: 100, sortable: true, renderer: money},
49305         {header: "Employees", width: 100, sortable: true, resizable: false}
49306  ]);
49307  </code></pre>
49308  * <p>
49309  
49310  * The config options listed for this class are options which may appear in each
49311  * individual column definition.
49312  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
49313  * @constructor
49314  * @param {Object} config An Array of column config objects. See this class's
49315  * config objects for details.
49316 */
49317 Roo.grid.ColumnModel = function(config){
49318         /**
49319      * The config passed into the constructor
49320      */
49321     this.config = config;
49322     this.lookup = {};
49323
49324     // if no id, create one
49325     // if the column does not have a dataIndex mapping,
49326     // map it to the order it is in the config
49327     for(var i = 0, len = config.length; i < len; i++){
49328         var c = config[i];
49329         if(typeof c.dataIndex == "undefined"){
49330             c.dataIndex = i;
49331         }
49332         if(typeof c.renderer == "string"){
49333             c.renderer = Roo.util.Format[c.renderer];
49334         }
49335         if(typeof c.id == "undefined"){
49336             c.id = Roo.id();
49337         }
49338         if(c.editor && c.editor.xtype){
49339             c.editor  = Roo.factory(c.editor, Roo.grid);
49340         }
49341         if(c.editor && c.editor.isFormField){
49342             c.editor = new Roo.grid.GridEditor(c.editor);
49343         }
49344         this.lookup[c.id] = c;
49345     }
49346
49347     /**
49348      * The width of columns which have no width specified (defaults to 100)
49349      * @type Number
49350      */
49351     this.defaultWidth = 100;
49352
49353     /**
49354      * Default sortable of columns which have no sortable specified (defaults to false)
49355      * @type Boolean
49356      */
49357     this.defaultSortable = false;
49358
49359     this.addEvents({
49360         /**
49361              * @event widthchange
49362              * Fires when the width of a column changes.
49363              * @param {ColumnModel} this
49364              * @param {Number} columnIndex The column index
49365              * @param {Number} newWidth The new width
49366              */
49367             "widthchange": true,
49368         /**
49369              * @event headerchange
49370              * Fires when the text of a header changes.
49371              * @param {ColumnModel} this
49372              * @param {Number} columnIndex The column index
49373              * @param {Number} newText The new header text
49374              */
49375             "headerchange": true,
49376         /**
49377              * @event hiddenchange
49378              * Fires when a column is hidden or "unhidden".
49379              * @param {ColumnModel} this
49380              * @param {Number} columnIndex The column index
49381              * @param {Boolean} hidden true if hidden, false otherwise
49382              */
49383             "hiddenchange": true,
49384             /**
49385          * @event columnmoved
49386          * Fires when a column is moved.
49387          * @param {ColumnModel} this
49388          * @param {Number} oldIndex
49389          * @param {Number} newIndex
49390          */
49391         "columnmoved" : true,
49392         /**
49393          * @event columlockchange
49394          * Fires when a column's locked state is changed
49395          * @param {ColumnModel} this
49396          * @param {Number} colIndex
49397          * @param {Boolean} locked true if locked
49398          */
49399         "columnlockchange" : true
49400     });
49401     Roo.grid.ColumnModel.superclass.constructor.call(this);
49402 };
49403 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
49404     /**
49405      * @cfg {String} header The header text to display in the Grid view.
49406      */
49407     /**
49408      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
49409      * {@link Roo.data.Record} definition from which to draw the column's value. If not
49410      * specified, the column's index is used as an index into the Record's data Array.
49411      */
49412     /**
49413      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
49414      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
49415      */
49416     /**
49417      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
49418      * Defaults to the value of the {@link #defaultSortable} property.
49419      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
49420      */
49421     /**
49422      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
49423      */
49424     /**
49425      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
49426      */
49427     /**
49428      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
49429      */
49430     /**
49431      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
49432      */
49433     /**
49434      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
49435      * given the cell's data value. See {@link #setRenderer}. If not specified, the
49436      * default renderer uses the raw data value.
49437      */
49438        /**
49439      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
49440      */
49441     /**
49442      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
49443      */
49444
49445     /**
49446      * Returns the id of the column at the specified index.
49447      * @param {Number} index The column index
49448      * @return {String} the id
49449      */
49450     getColumnId : function(index){
49451         return this.config[index].id;
49452     },
49453
49454     /**
49455      * Returns the column for a specified id.
49456      * @param {String} id The column id
49457      * @return {Object} the column
49458      */
49459     getColumnById : function(id){
49460         return this.lookup[id];
49461     },
49462
49463     
49464     /**
49465      * Returns the column for a specified dataIndex.
49466      * @param {String} dataIndex The column dataIndex
49467      * @return {Object|Boolean} the column or false if not found
49468      */
49469     getColumnByDataIndex: function(dataIndex){
49470         var index = this.findColumnIndex(dataIndex);
49471         return index > -1 ? this.config[index] : false;
49472     },
49473     
49474     /**
49475      * Returns the index for a specified column id.
49476      * @param {String} id The column id
49477      * @return {Number} the index, or -1 if not found
49478      */
49479     getIndexById : function(id){
49480         for(var i = 0, len = this.config.length; i < len; i++){
49481             if(this.config[i].id == id){
49482                 return i;
49483             }
49484         }
49485         return -1;
49486     },
49487     
49488     /**
49489      * Returns the index for a specified column dataIndex.
49490      * @param {String} dataIndex The column dataIndex
49491      * @return {Number} the index, or -1 if not found
49492      */
49493     
49494     findColumnIndex : function(dataIndex){
49495         for(var i = 0, len = this.config.length; i < len; i++){
49496             if(this.config[i].dataIndex == dataIndex){
49497                 return i;
49498             }
49499         }
49500         return -1;
49501     },
49502     
49503     
49504     moveColumn : function(oldIndex, newIndex){
49505         var c = this.config[oldIndex];
49506         this.config.splice(oldIndex, 1);
49507         this.config.splice(newIndex, 0, c);
49508         this.dataMap = null;
49509         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49510     },
49511
49512     isLocked : function(colIndex){
49513         return this.config[colIndex].locked === true;
49514     },
49515
49516     setLocked : function(colIndex, value, suppressEvent){
49517         if(this.isLocked(colIndex) == value){
49518             return;
49519         }
49520         this.config[colIndex].locked = value;
49521         if(!suppressEvent){
49522             this.fireEvent("columnlockchange", this, colIndex, value);
49523         }
49524     },
49525
49526     getTotalLockedWidth : function(){
49527         var totalWidth = 0;
49528         for(var i = 0; i < this.config.length; i++){
49529             if(this.isLocked(i) && !this.isHidden(i)){
49530                 this.totalWidth += this.getColumnWidth(i);
49531             }
49532         }
49533         return totalWidth;
49534     },
49535
49536     getLockedCount : function(){
49537         for(var i = 0, len = this.config.length; i < len; i++){
49538             if(!this.isLocked(i)){
49539                 return i;
49540             }
49541         }
49542     },
49543
49544     /**
49545      * Returns the number of columns.
49546      * @return {Number}
49547      */
49548     getColumnCount : function(visibleOnly){
49549         if(visibleOnly === true){
49550             var c = 0;
49551             for(var i = 0, len = this.config.length; i < len; i++){
49552                 if(!this.isHidden(i)){
49553                     c++;
49554                 }
49555             }
49556             return c;
49557         }
49558         return this.config.length;
49559     },
49560
49561     /**
49562      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49563      * @param {Function} fn
49564      * @param {Object} scope (optional)
49565      * @return {Array} result
49566      */
49567     getColumnsBy : function(fn, scope){
49568         var r = [];
49569         for(var i = 0, len = this.config.length; i < len; i++){
49570             var c = this.config[i];
49571             if(fn.call(scope||this, c, i) === true){
49572                 r[r.length] = c;
49573             }
49574         }
49575         return r;
49576     },
49577
49578     /**
49579      * Returns true if the specified column is sortable.
49580      * @param {Number} col The column index
49581      * @return {Boolean}
49582      */
49583     isSortable : function(col){
49584         if(typeof this.config[col].sortable == "undefined"){
49585             return this.defaultSortable;
49586         }
49587         return this.config[col].sortable;
49588     },
49589
49590     /**
49591      * Returns the rendering (formatting) function defined for the column.
49592      * @param {Number} col The column index.
49593      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49594      */
49595     getRenderer : function(col){
49596         if(!this.config[col].renderer){
49597             return Roo.grid.ColumnModel.defaultRenderer;
49598         }
49599         return this.config[col].renderer;
49600     },
49601
49602     /**
49603      * Sets the rendering (formatting) function for a column.
49604      * @param {Number} col The column index
49605      * @param {Function} fn The function to use to process the cell's raw data
49606      * to return HTML markup for the grid view. The render function is called with
49607      * the following parameters:<ul>
49608      * <li>Data value.</li>
49609      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49610      * <li>css A CSS style string to apply to the table cell.</li>
49611      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49612      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49613      * <li>Row index</li>
49614      * <li>Column index</li>
49615      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49616      */
49617     setRenderer : function(col, fn){
49618         this.config[col].renderer = fn;
49619     },
49620
49621     /**
49622      * Returns the width for the specified column.
49623      * @param {Number} col The column index
49624      * @return {Number}
49625      */
49626     getColumnWidth : function(col){
49627         return this.config[col].width * 1 || this.defaultWidth;
49628     },
49629
49630     /**
49631      * Sets the width for a column.
49632      * @param {Number} col The column index
49633      * @param {Number} width The new width
49634      */
49635     setColumnWidth : function(col, width, suppressEvent){
49636         this.config[col].width = width;
49637         this.totalWidth = null;
49638         if(!suppressEvent){
49639              this.fireEvent("widthchange", this, col, width);
49640         }
49641     },
49642
49643     /**
49644      * Returns the total width of all columns.
49645      * @param {Boolean} includeHidden True to include hidden column widths
49646      * @return {Number}
49647      */
49648     getTotalWidth : function(includeHidden){
49649         if(!this.totalWidth){
49650             this.totalWidth = 0;
49651             for(var i = 0, len = this.config.length; i < len; i++){
49652                 if(includeHidden || !this.isHidden(i)){
49653                     this.totalWidth += this.getColumnWidth(i);
49654                 }
49655             }
49656         }
49657         return this.totalWidth;
49658     },
49659
49660     /**
49661      * Returns the header for the specified column.
49662      * @param {Number} col The column index
49663      * @return {String}
49664      */
49665     getColumnHeader : function(col){
49666         return this.config[col].header;
49667     },
49668
49669     /**
49670      * Sets the header for a column.
49671      * @param {Number} col The column index
49672      * @param {String} header The new header
49673      */
49674     setColumnHeader : function(col, header){
49675         this.config[col].header = header;
49676         this.fireEvent("headerchange", this, col, header);
49677     },
49678
49679     /**
49680      * Returns the tooltip for the specified column.
49681      * @param {Number} col The column index
49682      * @return {String}
49683      */
49684     getColumnTooltip : function(col){
49685             return this.config[col].tooltip;
49686     },
49687     /**
49688      * Sets the tooltip for a column.
49689      * @param {Number} col The column index
49690      * @param {String} tooltip The new tooltip
49691      */
49692     setColumnTooltip : function(col, tooltip){
49693             this.config[col].tooltip = tooltip;
49694     },
49695
49696     /**
49697      * Returns the dataIndex for the specified column.
49698      * @param {Number} col The column index
49699      * @return {Number}
49700      */
49701     getDataIndex : function(col){
49702         return this.config[col].dataIndex;
49703     },
49704
49705     /**
49706      * Sets the dataIndex for a column.
49707      * @param {Number} col The column index
49708      * @param {Number} dataIndex The new dataIndex
49709      */
49710     setDataIndex : function(col, dataIndex){
49711         this.config[col].dataIndex = dataIndex;
49712     },
49713
49714     
49715     
49716     /**
49717      * Returns true if the cell is editable.
49718      * @param {Number} colIndex The column index
49719      * @param {Number} rowIndex The row index
49720      * @return {Boolean}
49721      */
49722     isCellEditable : function(colIndex, rowIndex){
49723         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49724     },
49725
49726     /**
49727      * Returns the editor defined for the cell/column.
49728      * return false or null to disable editing.
49729      * @param {Number} colIndex The column index
49730      * @param {Number} rowIndex The row index
49731      * @return {Object}
49732      */
49733     getCellEditor : function(colIndex, rowIndex){
49734         return this.config[colIndex].editor;
49735     },
49736
49737     /**
49738      * Sets if a column is editable.
49739      * @param {Number} col The column index
49740      * @param {Boolean} editable True if the column is editable
49741      */
49742     setEditable : function(col, editable){
49743         this.config[col].editable = editable;
49744     },
49745
49746
49747     /**
49748      * Returns true if the column is hidden.
49749      * @param {Number} colIndex The column index
49750      * @return {Boolean}
49751      */
49752     isHidden : function(colIndex){
49753         return this.config[colIndex].hidden;
49754     },
49755
49756
49757     /**
49758      * Returns true if the column width cannot be changed
49759      */
49760     isFixed : function(colIndex){
49761         return this.config[colIndex].fixed;
49762     },
49763
49764     /**
49765      * Returns true if the column can be resized
49766      * @return {Boolean}
49767      */
49768     isResizable : function(colIndex){
49769         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49770     },
49771     /**
49772      * Sets if a column is hidden.
49773      * @param {Number} colIndex The column index
49774      * @param {Boolean} hidden True if the column is hidden
49775      */
49776     setHidden : function(colIndex, hidden){
49777         this.config[colIndex].hidden = hidden;
49778         this.totalWidth = null;
49779         this.fireEvent("hiddenchange", this, colIndex, hidden);
49780     },
49781
49782     /**
49783      * Sets the editor for a column.
49784      * @param {Number} col The column index
49785      * @param {Object} editor The editor object
49786      */
49787     setEditor : function(col, editor){
49788         this.config[col].editor = editor;
49789     }
49790 });
49791
49792 Roo.grid.ColumnModel.defaultRenderer = function(value){
49793         if(typeof value == "string" && value.length < 1){
49794             return "&#160;";
49795         }
49796         return value;
49797 };
49798
49799 // Alias for backwards compatibility
49800 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49801 /*
49802  * Based on:
49803  * Ext JS Library 1.1.1
49804  * Copyright(c) 2006-2007, Ext JS, LLC.
49805  *
49806  * Originally Released Under LGPL - original licence link has changed is not relivant.
49807  *
49808  * Fork - LGPL
49809  * <script type="text/javascript">
49810  */
49811
49812 /**
49813  * @class Roo.grid.AbstractSelectionModel
49814  * @extends Roo.util.Observable
49815  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49816  * implemented by descendant classes.  This class should not be directly instantiated.
49817  * @constructor
49818  */
49819 Roo.grid.AbstractSelectionModel = function(){
49820     this.locked = false;
49821     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49822 };
49823
49824 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49825     /** @ignore Called by the grid automatically. Do not call directly. */
49826     init : function(grid){
49827         this.grid = grid;
49828         this.initEvents();
49829     },
49830
49831     /**
49832      * Locks the selections.
49833      */
49834     lock : function(){
49835         this.locked = true;
49836     },
49837
49838     /**
49839      * Unlocks the selections.
49840      */
49841     unlock : function(){
49842         this.locked = false;
49843     },
49844
49845     /**
49846      * Returns true if the selections are locked.
49847      * @return {Boolean}
49848      */
49849     isLocked : function(){
49850         return this.locked;
49851     }
49852 });/*
49853  * Based on:
49854  * Ext JS Library 1.1.1
49855  * Copyright(c) 2006-2007, Ext JS, LLC.
49856  *
49857  * Originally Released Under LGPL - original licence link has changed is not relivant.
49858  *
49859  * Fork - LGPL
49860  * <script type="text/javascript">
49861  */
49862 /**
49863  * @extends Roo.grid.AbstractSelectionModel
49864  * @class Roo.grid.RowSelectionModel
49865  * The default SelectionModel used by {@link Roo.grid.Grid}.
49866  * It supports multiple selections and keyboard selection/navigation. 
49867  * @constructor
49868  * @param {Object} config
49869  */
49870 Roo.grid.RowSelectionModel = function(config){
49871     Roo.apply(this, config);
49872     this.selections = new Roo.util.MixedCollection(false, function(o){
49873         return o.id;
49874     });
49875
49876     this.last = false;
49877     this.lastActive = false;
49878
49879     this.addEvents({
49880         /**
49881              * @event selectionchange
49882              * Fires when the selection changes
49883              * @param {SelectionModel} this
49884              */
49885             "selectionchange" : true,
49886         /**
49887              * @event afterselectionchange
49888              * Fires after the selection changes (eg. by key press or clicking)
49889              * @param {SelectionModel} this
49890              */
49891             "afterselectionchange" : true,
49892         /**
49893              * @event beforerowselect
49894              * Fires when a row is selected being selected, return false to cancel.
49895              * @param {SelectionModel} this
49896              * @param {Number} rowIndex The selected index
49897              * @param {Boolean} keepExisting False if other selections will be cleared
49898              */
49899             "beforerowselect" : true,
49900         /**
49901              * @event rowselect
49902              * Fires when a row is selected.
49903              * @param {SelectionModel} this
49904              * @param {Number} rowIndex The selected index
49905              * @param {Roo.data.Record} r The record
49906              */
49907             "rowselect" : true,
49908         /**
49909              * @event rowdeselect
49910              * Fires when a row is deselected.
49911              * @param {SelectionModel} this
49912              * @param {Number} rowIndex The selected index
49913              */
49914         "rowdeselect" : true
49915     });
49916     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49917     this.locked = false;
49918 };
49919
49920 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49921     /**
49922      * @cfg {Boolean} singleSelect
49923      * True to allow selection of only one row at a time (defaults to false)
49924      */
49925     singleSelect : false,
49926
49927     // private
49928     initEvents : function(){
49929
49930         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49931             this.grid.on("mousedown", this.handleMouseDown, this);
49932         }else{ // allow click to work like normal
49933             this.grid.on("rowclick", this.handleDragableRowClick, this);
49934         }
49935
49936         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49937             "up" : function(e){
49938                 if(!e.shiftKey){
49939                     this.selectPrevious(e.shiftKey);
49940                 }else if(this.last !== false && this.lastActive !== false){
49941                     var last = this.last;
49942                     this.selectRange(this.last,  this.lastActive-1);
49943                     this.grid.getView().focusRow(this.lastActive);
49944                     if(last !== false){
49945                         this.last = last;
49946                     }
49947                 }else{
49948                     this.selectFirstRow();
49949                 }
49950                 this.fireEvent("afterselectionchange", this);
49951             },
49952             "down" : function(e){
49953                 if(!e.shiftKey){
49954                     this.selectNext(e.shiftKey);
49955                 }else if(this.last !== false && this.lastActive !== false){
49956                     var last = this.last;
49957                     this.selectRange(this.last,  this.lastActive+1);
49958                     this.grid.getView().focusRow(this.lastActive);
49959                     if(last !== false){
49960                         this.last = last;
49961                     }
49962                 }else{
49963                     this.selectFirstRow();
49964                 }
49965                 this.fireEvent("afterselectionchange", this);
49966             },
49967             scope: this
49968         });
49969
49970         var view = this.grid.view;
49971         view.on("refresh", this.onRefresh, this);
49972         view.on("rowupdated", this.onRowUpdated, this);
49973         view.on("rowremoved", this.onRemove, this);
49974     },
49975
49976     // private
49977     onRefresh : function(){
49978         var ds = this.grid.dataSource, i, v = this.grid.view;
49979         var s = this.selections;
49980         s.each(function(r){
49981             if((i = ds.indexOfId(r.id)) != -1){
49982                 v.onRowSelect(i);
49983             }else{
49984                 s.remove(r);
49985             }
49986         });
49987     },
49988
49989     // private
49990     onRemove : function(v, index, r){
49991         this.selections.remove(r);
49992     },
49993
49994     // private
49995     onRowUpdated : function(v, index, r){
49996         if(this.isSelected(r)){
49997             v.onRowSelect(index);
49998         }
49999     },
50000
50001     /**
50002      * Select records.
50003      * @param {Array} records The records to select
50004      * @param {Boolean} keepExisting (optional) True to keep existing selections
50005      */
50006     selectRecords : function(records, keepExisting){
50007         if(!keepExisting){
50008             this.clearSelections();
50009         }
50010         var ds = this.grid.dataSource;
50011         for(var i = 0, len = records.length; i < len; i++){
50012             this.selectRow(ds.indexOf(records[i]), true);
50013         }
50014     },
50015
50016     /**
50017      * Gets the number of selected rows.
50018      * @return {Number}
50019      */
50020     getCount : function(){
50021         return this.selections.length;
50022     },
50023
50024     /**
50025      * Selects the first row in the grid.
50026      */
50027     selectFirstRow : function(){
50028         this.selectRow(0);
50029     },
50030
50031     /**
50032      * Select the last row.
50033      * @param {Boolean} keepExisting (optional) True to keep existing selections
50034      */
50035     selectLastRow : function(keepExisting){
50036         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50037     },
50038
50039     /**
50040      * Selects the row immediately following the last selected row.
50041      * @param {Boolean} keepExisting (optional) True to keep existing selections
50042      */
50043     selectNext : function(keepExisting){
50044         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50045             this.selectRow(this.last+1, keepExisting);
50046             this.grid.getView().focusRow(this.last);
50047         }
50048     },
50049
50050     /**
50051      * Selects the row that precedes the last selected row.
50052      * @param {Boolean} keepExisting (optional) True to keep existing selections
50053      */
50054     selectPrevious : function(keepExisting){
50055         if(this.last){
50056             this.selectRow(this.last-1, keepExisting);
50057             this.grid.getView().focusRow(this.last);
50058         }
50059     },
50060
50061     /**
50062      * Returns the selected records
50063      * @return {Array} Array of selected records
50064      */
50065     getSelections : function(){
50066         return [].concat(this.selections.items);
50067     },
50068
50069     /**
50070      * Returns the first selected record.
50071      * @return {Record}
50072      */
50073     getSelected : function(){
50074         return this.selections.itemAt(0);
50075     },
50076
50077
50078     /**
50079      * Clears all selections.
50080      */
50081     clearSelections : function(fast){
50082         if(this.locked) return;
50083         if(fast !== true){
50084             var ds = this.grid.dataSource;
50085             var s = this.selections;
50086             s.each(function(r){
50087                 this.deselectRow(ds.indexOfId(r.id));
50088             }, this);
50089             s.clear();
50090         }else{
50091             this.selections.clear();
50092         }
50093         this.last = false;
50094     },
50095
50096
50097     /**
50098      * Selects all rows.
50099      */
50100     selectAll : function(){
50101         if(this.locked) return;
50102         this.selections.clear();
50103         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50104             this.selectRow(i, true);
50105         }
50106     },
50107
50108     /**
50109      * Returns True if there is a selection.
50110      * @return {Boolean}
50111      */
50112     hasSelection : function(){
50113         return this.selections.length > 0;
50114     },
50115
50116     /**
50117      * Returns True if the specified row is selected.
50118      * @param {Number/Record} record The record or index of the record to check
50119      * @return {Boolean}
50120      */
50121     isSelected : function(index){
50122         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50123         return (r && this.selections.key(r.id) ? true : false);
50124     },
50125
50126     /**
50127      * Returns True if the specified record id is selected.
50128      * @param {String} id The id of record to check
50129      * @return {Boolean}
50130      */
50131     isIdSelected : function(id){
50132         return (this.selections.key(id) ? true : false);
50133     },
50134
50135     // private
50136     handleMouseDown : function(e, t){
50137         var view = this.grid.getView(), rowIndex;
50138         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50139             return;
50140         };
50141         if(e.shiftKey && this.last !== false){
50142             var last = this.last;
50143             this.selectRange(last, rowIndex, e.ctrlKey);
50144             this.last = last; // reset the last
50145             view.focusRow(rowIndex);
50146         }else{
50147             var isSelected = this.isSelected(rowIndex);
50148             if(e.button !== 0 && isSelected){
50149                 view.focusRow(rowIndex);
50150             }else if(e.ctrlKey && isSelected){
50151                 this.deselectRow(rowIndex);
50152             }else if(!isSelected){
50153                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50154                 view.focusRow(rowIndex);
50155             }
50156         }
50157         this.fireEvent("afterselectionchange", this);
50158     },
50159     // private
50160     handleDragableRowClick :  function(grid, rowIndex, e) 
50161     {
50162         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50163             this.selectRow(rowIndex, false);
50164             grid.view.focusRow(rowIndex);
50165              this.fireEvent("afterselectionchange", this);
50166         }
50167     },
50168     
50169     /**
50170      * Selects multiple rows.
50171      * @param {Array} rows Array of the indexes of the row to select
50172      * @param {Boolean} keepExisting (optional) True to keep existing selections
50173      */
50174     selectRows : function(rows, keepExisting){
50175         if(!keepExisting){
50176             this.clearSelections();
50177         }
50178         for(var i = 0, len = rows.length; i < len; i++){
50179             this.selectRow(rows[i], true);
50180         }
50181     },
50182
50183     /**
50184      * Selects a range of rows. All rows in between startRow and endRow are also selected.
50185      * @param {Number} startRow The index of the first row in the range
50186      * @param {Number} endRow The index of the last row in the range
50187      * @param {Boolean} keepExisting (optional) True to retain existing selections
50188      */
50189     selectRange : function(startRow, endRow, keepExisting){
50190         if(this.locked) return;
50191         if(!keepExisting){
50192             this.clearSelections();
50193         }
50194         if(startRow <= endRow){
50195             for(var i = startRow; i <= endRow; i++){
50196                 this.selectRow(i, true);
50197             }
50198         }else{
50199             for(var i = startRow; i >= endRow; i--){
50200                 this.selectRow(i, true);
50201             }
50202         }
50203     },
50204
50205     /**
50206      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
50207      * @param {Number} startRow The index of the first row in the range
50208      * @param {Number} endRow The index of the last row in the range
50209      */
50210     deselectRange : function(startRow, endRow, preventViewNotify){
50211         if(this.locked) return;
50212         for(var i = startRow; i <= endRow; i++){
50213             this.deselectRow(i, preventViewNotify);
50214         }
50215     },
50216
50217     /**
50218      * Selects a row.
50219      * @param {Number} row The index of the row to select
50220      * @param {Boolean} keepExisting (optional) True to keep existing selections
50221      */
50222     selectRow : function(index, keepExisting, preventViewNotify){
50223         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
50224         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
50225             if(!keepExisting || this.singleSelect){
50226                 this.clearSelections();
50227             }
50228             var r = this.grid.dataSource.getAt(index);
50229             this.selections.add(r);
50230             this.last = this.lastActive = index;
50231             if(!preventViewNotify){
50232                 this.grid.getView().onRowSelect(index);
50233             }
50234             this.fireEvent("rowselect", this, index, r);
50235             this.fireEvent("selectionchange", this);
50236         }
50237     },
50238
50239     /**
50240      * Deselects a row.
50241      * @param {Number} row The index of the row to deselect
50242      */
50243     deselectRow : function(index, preventViewNotify){
50244         if(this.locked) return;
50245         if(this.last == index){
50246             this.last = false;
50247         }
50248         if(this.lastActive == index){
50249             this.lastActive = false;
50250         }
50251         var r = this.grid.dataSource.getAt(index);
50252         this.selections.remove(r);
50253         if(!preventViewNotify){
50254             this.grid.getView().onRowDeselect(index);
50255         }
50256         this.fireEvent("rowdeselect", this, index);
50257         this.fireEvent("selectionchange", this);
50258     },
50259
50260     // private
50261     restoreLast : function(){
50262         if(this._last){
50263             this.last = this._last;
50264         }
50265     },
50266
50267     // private
50268     acceptsNav : function(row, col, cm){
50269         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50270     },
50271
50272     // private
50273     onEditorKey : function(field, e){
50274         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50275         if(k == e.TAB){
50276             e.stopEvent();
50277             ed.completeEdit();
50278             if(e.shiftKey){
50279                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50280             }else{
50281                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50282             }
50283         }else if(k == e.ENTER && !e.ctrlKey){
50284             e.stopEvent();
50285             ed.completeEdit();
50286             if(e.shiftKey){
50287                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
50288             }else{
50289                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
50290             }
50291         }else if(k == e.ESC){
50292             ed.cancelEdit();
50293         }
50294         if(newCell){
50295             g.startEditing(newCell[0], newCell[1]);
50296         }
50297     }
50298 });/*
50299  * Based on:
50300  * Ext JS Library 1.1.1
50301  * Copyright(c) 2006-2007, Ext JS, LLC.
50302  *
50303  * Originally Released Under LGPL - original licence link has changed is not relivant.
50304  *
50305  * Fork - LGPL
50306  * <script type="text/javascript">
50307  */
50308 /**
50309  * @class Roo.grid.CellSelectionModel
50310  * @extends Roo.grid.AbstractSelectionModel
50311  * This class provides the basic implementation for cell selection in a grid.
50312  * @constructor
50313  * @param {Object} config The object containing the configuration of this model.
50314  */
50315 Roo.grid.CellSelectionModel = function(config){
50316     Roo.apply(this, config);
50317
50318     this.selection = null;
50319
50320     this.addEvents({
50321         /**
50322              * @event beforerowselect
50323              * Fires before a cell is selected.
50324              * @param {SelectionModel} this
50325              * @param {Number} rowIndex The selected row index
50326              * @param {Number} colIndex The selected cell index
50327              */
50328             "beforecellselect" : true,
50329         /**
50330              * @event cellselect
50331              * Fires when a cell is selected.
50332              * @param {SelectionModel} this
50333              * @param {Number} rowIndex The selected row index
50334              * @param {Number} colIndex The selected cell index
50335              */
50336             "cellselect" : true,
50337         /**
50338              * @event selectionchange
50339              * Fires when the active selection changes.
50340              * @param {SelectionModel} this
50341              * @param {Object} selection null for no selection or an object (o) with two properties
50342                 <ul>
50343                 <li>o.record: the record object for the row the selection is in</li>
50344                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
50345                 </ul>
50346              */
50347             "selectionchange" : true
50348     });
50349     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
50350 };
50351
50352 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
50353
50354     /** @ignore */
50355     initEvents : function(){
50356         this.grid.on("mousedown", this.handleMouseDown, this);
50357         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
50358         var view = this.grid.view;
50359         view.on("refresh", this.onViewChange, this);
50360         view.on("rowupdated", this.onRowUpdated, this);
50361         view.on("beforerowremoved", this.clearSelections, this);
50362         view.on("beforerowsinserted", this.clearSelections, this);
50363         if(this.grid.isEditor){
50364             this.grid.on("beforeedit", this.beforeEdit,  this);
50365         }
50366     },
50367
50368         //private
50369     beforeEdit : function(e){
50370         this.select(e.row, e.column, false, true, e.record);
50371     },
50372
50373         //private
50374     onRowUpdated : function(v, index, r){
50375         if(this.selection && this.selection.record == r){
50376             v.onCellSelect(index, this.selection.cell[1]);
50377         }
50378     },
50379
50380         //private
50381     onViewChange : function(){
50382         this.clearSelections(true);
50383     },
50384
50385         /**
50386          * Returns the currently selected cell,.
50387          * @return {Array} The selected cell (row, column) or null if none selected.
50388          */
50389     getSelectedCell : function(){
50390         return this.selection ? this.selection.cell : null;
50391     },
50392
50393     /**
50394      * Clears all selections.
50395      * @param {Boolean} true to prevent the gridview from being notified about the change.
50396      */
50397     clearSelections : function(preventNotify){
50398         var s = this.selection;
50399         if(s){
50400             if(preventNotify !== true){
50401                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
50402             }
50403             this.selection = null;
50404             this.fireEvent("selectionchange", this, null);
50405         }
50406     },
50407
50408     /**
50409      * Returns true if there is a selection.
50410      * @return {Boolean}
50411      */
50412     hasSelection : function(){
50413         return this.selection ? true : false;
50414     },
50415
50416     /** @ignore */
50417     handleMouseDown : function(e, t){
50418         var v = this.grid.getView();
50419         if(this.isLocked()){
50420             return;
50421         };
50422         var row = v.findRowIndex(t);
50423         var cell = v.findCellIndex(t);
50424         if(row !== false && cell !== false){
50425             this.select(row, cell);
50426         }
50427     },
50428
50429     /**
50430      * Selects a cell.
50431      * @param {Number} rowIndex
50432      * @param {Number} collIndex
50433      */
50434     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
50435         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
50436             this.clearSelections();
50437             r = r || this.grid.dataSource.getAt(rowIndex);
50438             this.selection = {
50439                 record : r,
50440                 cell : [rowIndex, colIndex]
50441             };
50442             if(!preventViewNotify){
50443                 var v = this.grid.getView();
50444                 v.onCellSelect(rowIndex, colIndex);
50445                 if(preventFocus !== true){
50446                     v.focusCell(rowIndex, colIndex);
50447                 }
50448             }
50449             this.fireEvent("cellselect", this, rowIndex, colIndex);
50450             this.fireEvent("selectionchange", this, this.selection);
50451         }
50452     },
50453
50454         //private
50455     isSelectable : function(rowIndex, colIndex, cm){
50456         return !cm.isHidden(colIndex);
50457     },
50458
50459     /** @ignore */
50460     handleKeyDown : function(e){
50461         Roo.log('Cell Sel Model handleKeyDown');
50462         if(!e.isNavKeyPress()){
50463             return;
50464         }
50465         var g = this.grid, s = this.selection;
50466         if(!s){
50467             e.stopEvent();
50468             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
50469             if(cell){
50470                 this.select(cell[0], cell[1]);
50471             }
50472             return;
50473         }
50474         var sm = this;
50475         var walk = function(row, col, step){
50476             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50477         };
50478         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50479         var newCell;
50480
50481         switch(k){
50482             case e.TAB:
50483                 // handled by onEditorKey
50484                 if (g.isEditor && g.editing) {
50485                     return;
50486                 }
50487                 if(e.shiftKey){
50488                      newCell = walk(r, c-1, -1);
50489                 }else{
50490                      newCell = walk(r, c+1, 1);
50491                 }
50492              break;
50493              case e.DOWN:
50494                  newCell = walk(r+1, c, 1);
50495              break;
50496              case e.UP:
50497                  newCell = walk(r-1, c, -1);
50498              break;
50499              case e.RIGHT:
50500                  newCell = walk(r, c+1, 1);
50501              break;
50502              case e.LEFT:
50503                  newCell = walk(r, c-1, -1);
50504              break;
50505              case e.ENTER:
50506                  if(g.isEditor && !g.editing){
50507                     g.startEditing(r, c);
50508                     e.stopEvent();
50509                     return;
50510                 }
50511              break;
50512         };
50513         if(newCell){
50514             this.select(newCell[0], newCell[1]);
50515             e.stopEvent();
50516         }
50517     },
50518
50519     acceptsNav : function(row, col, cm){
50520         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50521     },
50522
50523     onEditorKey : function(field, e){
50524         
50525         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50526         ///Roo.log('onEditorKey' + k);
50527         
50528         if(k == e.TAB){
50529             if(e.shiftKey){
50530                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50531             }else{
50532                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50533             }
50534             e.stopEvent();
50535         }else if(k == e.ENTER && !e.ctrlKey){
50536             ed.completeEdit();
50537             e.stopEvent();
50538             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50539         }else if(k == e.ESC){
50540             ed.cancelEdit();
50541         }
50542         
50543         
50544         if(newCell){
50545             //Roo.log('next cell after edit');
50546             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50547         }
50548     }
50549 });/*
50550  * Based on:
50551  * Ext JS Library 1.1.1
50552  * Copyright(c) 2006-2007, Ext JS, LLC.
50553  *
50554  * Originally Released Under LGPL - original licence link has changed is not relivant.
50555  *
50556  * Fork - LGPL
50557  * <script type="text/javascript">
50558  */
50559  
50560 /**
50561  * @class Roo.grid.EditorGrid
50562  * @extends Roo.grid.Grid
50563  * Class for creating and editable grid.
50564  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50565  * The container MUST have some type of size defined for the grid to fill. The container will be 
50566  * automatically set to position relative if it isn't already.
50567  * @param {Object} dataSource The data model to bind to
50568  * @param {Object} colModel The column model with info about this grid's columns
50569  */
50570 Roo.grid.EditorGrid = function(container, config){
50571     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50572     this.getGridEl().addClass("xedit-grid");
50573
50574     if(!this.selModel){
50575         this.selModel = new Roo.grid.CellSelectionModel();
50576     }
50577
50578     this.activeEditor = null;
50579
50580         this.addEvents({
50581             /**
50582              * @event beforeedit
50583              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50584              * <ul style="padding:5px;padding-left:16px;">
50585              * <li>grid - This grid</li>
50586              * <li>record - The record being edited</li>
50587              * <li>field - The field name being edited</li>
50588              * <li>value - The value for the field being edited.</li>
50589              * <li>row - The grid row index</li>
50590              * <li>column - The grid column index</li>
50591              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50592              * </ul>
50593              * @param {Object} e An edit event (see above for description)
50594              */
50595             "beforeedit" : true,
50596             /**
50597              * @event afteredit
50598              * Fires after a cell is edited. <br />
50599              * <ul style="padding:5px;padding-left:16px;">
50600              * <li>grid - This grid</li>
50601              * <li>record - The record being edited</li>
50602              * <li>field - The field name being edited</li>
50603              * <li>value - The value being set</li>
50604              * <li>originalValue - The original value for the field, before the edit.</li>
50605              * <li>row - The grid row index</li>
50606              * <li>column - The grid column index</li>
50607              * </ul>
50608              * @param {Object} e An edit event (see above for description)
50609              */
50610             "afteredit" : true,
50611             /**
50612              * @event validateedit
50613              * Fires after a cell is edited, but before the value is set in the record. 
50614          * You can use this to modify the value being set in the field, Return false
50615              * to cancel the change. The edit event object has the following properties <br />
50616              * <ul style="padding:5px;padding-left:16px;">
50617          * <li>editor - This editor</li>
50618              * <li>grid - This grid</li>
50619              * <li>record - The record being edited</li>
50620              * <li>field - The field name being edited</li>
50621              * <li>value - The value being set</li>
50622              * <li>originalValue - The original value for the field, before the edit.</li>
50623              * <li>row - The grid row index</li>
50624              * <li>column - The grid column index</li>
50625              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50626              * </ul>
50627              * @param {Object} e An edit event (see above for description)
50628              */
50629             "validateedit" : true
50630         });
50631     this.on("bodyscroll", this.stopEditing,  this);
50632     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50633 };
50634
50635 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50636     /**
50637      * @cfg {Number} clicksToEdit
50638      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50639      */
50640     clicksToEdit: 2,
50641
50642     // private
50643     isEditor : true,
50644     // private
50645     trackMouseOver: false, // causes very odd FF errors
50646
50647     onCellDblClick : function(g, row, col){
50648         this.startEditing(row, col);
50649     },
50650
50651     onEditComplete : function(ed, value, startValue){
50652         this.editing = false;
50653         this.activeEditor = null;
50654         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50655         var r = ed.record;
50656         var field = this.colModel.getDataIndex(ed.col);
50657         var e = {
50658             grid: this,
50659             record: r,
50660             field: field,
50661             originalValue: startValue,
50662             value: value,
50663             row: ed.row,
50664             column: ed.col,
50665             cancel:false,
50666             editor: ed
50667         };
50668         if(String(value) !== String(startValue)){
50669             
50670             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50671                 r.set(field, e.value);
50672                 // if we are dealing with a combo box..
50673                 // then we also set the 'name' colum to be the displayField
50674                 if (ed.field.displayField && ed.field.name) {
50675                     r.set(ed.field.name, ed.field.el.dom.value);
50676                 }
50677                 
50678                 delete e.cancel; //?? why!!!
50679                 this.fireEvent("afteredit", e);
50680             }
50681         } else {
50682             this.fireEvent("afteredit", e); // always fire it!
50683         }
50684         this.view.focusCell(ed.row, ed.col);
50685     },
50686
50687     /**
50688      * Starts editing the specified for the specified row/column
50689      * @param {Number} rowIndex
50690      * @param {Number} colIndex
50691      */
50692     startEditing : function(row, col){
50693         this.stopEditing();
50694         if(this.colModel.isCellEditable(col, row)){
50695             this.view.ensureVisible(row, col, true);
50696             var r = this.dataSource.getAt(row);
50697             var field = this.colModel.getDataIndex(col);
50698             var e = {
50699                 grid: this,
50700                 record: r,
50701                 field: field,
50702                 value: r.data[field],
50703                 row: row,
50704                 column: col,
50705                 cancel:false
50706             };
50707             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50708                 this.editing = true;
50709                 var ed = this.colModel.getCellEditor(col, row);
50710                 
50711                 if (!ed) {
50712                     return;
50713                 }
50714                 if(!ed.rendered){
50715                     ed.render(ed.parentEl || document.body);
50716                 }
50717                 ed.field.reset();
50718                 (function(){ // complex but required for focus issues in safari, ie and opera
50719                     ed.row = row;
50720                     ed.col = col;
50721                     ed.record = r;
50722                     ed.on("complete", this.onEditComplete, this, {single: true});
50723                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50724                     this.activeEditor = ed;
50725                     var v = r.data[field];
50726                     ed.startEdit(this.view.getCell(row, col), v);
50727                     // combo's with 'displayField and name set
50728                     if (ed.field.displayField && ed.field.name) {
50729                         ed.field.el.dom.value = r.data[ed.field.name];
50730                     }
50731                     
50732                     
50733                 }).defer(50, this);
50734             }
50735         }
50736     },
50737         
50738     /**
50739      * Stops any active editing
50740      */
50741     stopEditing : function(){
50742         if(this.activeEditor){
50743             this.activeEditor.completeEdit();
50744         }
50745         this.activeEditor = null;
50746     }
50747 });/*
50748  * Based on:
50749  * Ext JS Library 1.1.1
50750  * Copyright(c) 2006-2007, Ext JS, LLC.
50751  *
50752  * Originally Released Under LGPL - original licence link has changed is not relivant.
50753  *
50754  * Fork - LGPL
50755  * <script type="text/javascript">
50756  */
50757
50758 // private - not really -- you end up using it !
50759 // This is a support class used internally by the Grid components
50760
50761 /**
50762  * @class Roo.grid.GridEditor
50763  * @extends Roo.Editor
50764  * Class for creating and editable grid elements.
50765  * @param {Object} config any settings (must include field)
50766  */
50767 Roo.grid.GridEditor = function(field, config){
50768     if (!config && field.field) {
50769         config = field;
50770         field = Roo.factory(config.field, Roo.form);
50771     }
50772     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50773     field.monitorTab = false;
50774 };
50775
50776 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50777     
50778     /**
50779      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50780      */
50781     
50782     alignment: "tl-tl",
50783     autoSize: "width",
50784     hideEl : false,
50785     cls: "x-small-editor x-grid-editor",
50786     shim:false,
50787     shadow:"frame"
50788 });/*
50789  * Based on:
50790  * Ext JS Library 1.1.1
50791  * Copyright(c) 2006-2007, Ext JS, LLC.
50792  *
50793  * Originally Released Under LGPL - original licence link has changed is not relivant.
50794  *
50795  * Fork - LGPL
50796  * <script type="text/javascript">
50797  */
50798   
50799
50800   
50801 Roo.grid.PropertyRecord = Roo.data.Record.create([
50802     {name:'name',type:'string'},  'value'
50803 ]);
50804
50805
50806 Roo.grid.PropertyStore = function(grid, source){
50807     this.grid = grid;
50808     this.store = new Roo.data.Store({
50809         recordType : Roo.grid.PropertyRecord
50810     });
50811     this.store.on('update', this.onUpdate,  this);
50812     if(source){
50813         this.setSource(source);
50814     }
50815     Roo.grid.PropertyStore.superclass.constructor.call(this);
50816 };
50817
50818
50819
50820 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50821     setSource : function(o){
50822         this.source = o;
50823         this.store.removeAll();
50824         var data = [];
50825         for(var k in o){
50826             if(this.isEditableValue(o[k])){
50827                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50828             }
50829         }
50830         this.store.loadRecords({records: data}, {}, true);
50831     },
50832
50833     onUpdate : function(ds, record, type){
50834         if(type == Roo.data.Record.EDIT){
50835             var v = record.data['value'];
50836             var oldValue = record.modified['value'];
50837             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50838                 this.source[record.id] = v;
50839                 record.commit();
50840                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50841             }else{
50842                 record.reject();
50843             }
50844         }
50845     },
50846
50847     getProperty : function(row){
50848        return this.store.getAt(row);
50849     },
50850
50851     isEditableValue: function(val){
50852         if(val && val instanceof Date){
50853             return true;
50854         }else if(typeof val == 'object' || typeof val == 'function'){
50855             return false;
50856         }
50857         return true;
50858     },
50859
50860     setValue : function(prop, value){
50861         this.source[prop] = value;
50862         this.store.getById(prop).set('value', value);
50863     },
50864
50865     getSource : function(){
50866         return this.source;
50867     }
50868 });
50869
50870 Roo.grid.PropertyColumnModel = function(grid, store){
50871     this.grid = grid;
50872     var g = Roo.grid;
50873     g.PropertyColumnModel.superclass.constructor.call(this, [
50874         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50875         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50876     ]);
50877     this.store = store;
50878     this.bselect = Roo.DomHelper.append(document.body, {
50879         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50880             {tag: 'option', value: 'true', html: 'true'},
50881             {tag: 'option', value: 'false', html: 'false'}
50882         ]
50883     });
50884     Roo.id(this.bselect);
50885     var f = Roo.form;
50886     this.editors = {
50887         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50888         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50889         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50890         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50891         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50892     };
50893     this.renderCellDelegate = this.renderCell.createDelegate(this);
50894     this.renderPropDelegate = this.renderProp.createDelegate(this);
50895 };
50896
50897 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50898     
50899     
50900     nameText : 'Name',
50901     valueText : 'Value',
50902     
50903     dateFormat : 'm/j/Y',
50904     
50905     
50906     renderDate : function(dateVal){
50907         return dateVal.dateFormat(this.dateFormat);
50908     },
50909
50910     renderBool : function(bVal){
50911         return bVal ? 'true' : 'false';
50912     },
50913
50914     isCellEditable : function(colIndex, rowIndex){
50915         return colIndex == 1;
50916     },
50917
50918     getRenderer : function(col){
50919         return col == 1 ?
50920             this.renderCellDelegate : this.renderPropDelegate;
50921     },
50922
50923     renderProp : function(v){
50924         return this.getPropertyName(v);
50925     },
50926
50927     renderCell : function(val){
50928         var rv = val;
50929         if(val instanceof Date){
50930             rv = this.renderDate(val);
50931         }else if(typeof val == 'boolean'){
50932             rv = this.renderBool(val);
50933         }
50934         return Roo.util.Format.htmlEncode(rv);
50935     },
50936
50937     getPropertyName : function(name){
50938         var pn = this.grid.propertyNames;
50939         return pn && pn[name] ? pn[name] : name;
50940     },
50941
50942     getCellEditor : function(colIndex, rowIndex){
50943         var p = this.store.getProperty(rowIndex);
50944         var n = p.data['name'], val = p.data['value'];
50945         
50946         if(typeof(this.grid.customEditors[n]) == 'string'){
50947             return this.editors[this.grid.customEditors[n]];
50948         }
50949         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50950             return this.grid.customEditors[n];
50951         }
50952         if(val instanceof Date){
50953             return this.editors['date'];
50954         }else if(typeof val == 'number'){
50955             return this.editors['number'];
50956         }else if(typeof val == 'boolean'){
50957             return this.editors['boolean'];
50958         }else{
50959             return this.editors['string'];
50960         }
50961     }
50962 });
50963
50964 /**
50965  * @class Roo.grid.PropertyGrid
50966  * @extends Roo.grid.EditorGrid
50967  * This class represents the  interface of a component based property grid control.
50968  * <br><br>Usage:<pre><code>
50969  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50970       
50971  });
50972  // set any options
50973  grid.render();
50974  * </code></pre>
50975   
50976  * @constructor
50977  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50978  * The container MUST have some type of size defined for the grid to fill. The container will be
50979  * automatically set to position relative if it isn't already.
50980  * @param {Object} config A config object that sets properties on this grid.
50981  */
50982 Roo.grid.PropertyGrid = function(container, config){
50983     config = config || {};
50984     var store = new Roo.grid.PropertyStore(this);
50985     this.store = store;
50986     var cm = new Roo.grid.PropertyColumnModel(this, store);
50987     store.store.sort('name', 'ASC');
50988     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50989         ds: store.store,
50990         cm: cm,
50991         enableColLock:false,
50992         enableColumnMove:false,
50993         stripeRows:false,
50994         trackMouseOver: false,
50995         clicksToEdit:1
50996     }, config));
50997     this.getGridEl().addClass('x-props-grid');
50998     this.lastEditRow = null;
50999     this.on('columnresize', this.onColumnResize, this);
51000     this.addEvents({
51001          /**
51002              * @event beforepropertychange
51003              * Fires before a property changes (return false to stop?)
51004              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51005              * @param {String} id Record Id
51006              * @param {String} newval New Value
51007          * @param {String} oldval Old Value
51008              */
51009         "beforepropertychange": true,
51010         /**
51011              * @event propertychange
51012              * Fires after a property changes
51013              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51014              * @param {String} id Record Id
51015              * @param {String} newval New Value
51016          * @param {String} oldval Old Value
51017              */
51018         "propertychange": true
51019     });
51020     this.customEditors = this.customEditors || {};
51021 };
51022 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51023     
51024      /**
51025      * @cfg {Object} customEditors map of colnames=> custom editors.
51026      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51027      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51028      * false disables editing of the field.
51029          */
51030     
51031       /**
51032      * @cfg {Object} propertyNames map of property Names to their displayed value
51033          */
51034     
51035     render : function(){
51036         Roo.grid.PropertyGrid.superclass.render.call(this);
51037         this.autoSize.defer(100, this);
51038     },
51039
51040     autoSize : function(){
51041         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51042         if(this.view){
51043             this.view.fitColumns();
51044         }
51045     },
51046
51047     onColumnResize : function(){
51048         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51049         this.autoSize();
51050     },
51051     /**
51052      * Sets the data for the Grid
51053      * accepts a Key => Value object of all the elements avaiable.
51054      * @param {Object} data  to appear in grid.
51055      */
51056     setSource : function(source){
51057         this.store.setSource(source);
51058         //this.autoSize();
51059     },
51060     /**
51061      * Gets all the data from the grid.
51062      * @return {Object} data  data stored in grid
51063      */
51064     getSource : function(){
51065         return this.store.getSource();
51066     }
51067 });/*
51068  * Based on:
51069  * Ext JS Library 1.1.1
51070  * Copyright(c) 2006-2007, Ext JS, LLC.
51071  *
51072  * Originally Released Under LGPL - original licence link has changed is not relivant.
51073  *
51074  * Fork - LGPL
51075  * <script type="text/javascript">
51076  */
51077  
51078 /**
51079  * @class Roo.LoadMask
51080  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51081  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51082  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51083  * element's UpdateManager load indicator and will be destroyed after the initial load.
51084  * @constructor
51085  * Create a new LoadMask
51086  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51087  * @param {Object} config The config object
51088  */
51089 Roo.LoadMask = function(el, config){
51090     this.el = Roo.get(el);
51091     Roo.apply(this, config);
51092     if(this.store){
51093         this.store.on('beforeload', this.onBeforeLoad, this);
51094         this.store.on('load', this.onLoad, this);
51095         this.store.on('loadexception', this.onLoad, this);
51096         this.removeMask = false;
51097     }else{
51098         var um = this.el.getUpdateManager();
51099         um.showLoadIndicator = false; // disable the default indicator
51100         um.on('beforeupdate', this.onBeforeLoad, this);
51101         um.on('update', this.onLoad, this);
51102         um.on('failure', this.onLoad, this);
51103         this.removeMask = true;
51104     }
51105 };
51106
51107 Roo.LoadMask.prototype = {
51108     /**
51109      * @cfg {Boolean} removeMask
51110      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51111      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51112      */
51113     /**
51114      * @cfg {String} msg
51115      * The text to display in a centered loading message box (defaults to 'Loading...')
51116      */
51117     msg : 'Loading...',
51118     /**
51119      * @cfg {String} msgCls
51120      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51121      */
51122     msgCls : 'x-mask-loading',
51123
51124     /**
51125      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51126      * @type Boolean
51127      */
51128     disabled: false,
51129
51130     /**
51131      * Disables the mask to prevent it from being displayed
51132      */
51133     disable : function(){
51134        this.disabled = true;
51135     },
51136
51137     /**
51138      * Enables the mask so that it can be displayed
51139      */
51140     enable : function(){
51141         this.disabled = false;
51142     },
51143
51144     // private
51145     onLoad : function(){
51146         this.el.unmask(this.removeMask);
51147     },
51148
51149     // private
51150     onBeforeLoad : function(){
51151         if(!this.disabled){
51152             this.el.mask(this.msg, this.msgCls);
51153         }
51154     },
51155
51156     // private
51157     destroy : function(){
51158         if(this.store){
51159             this.store.un('beforeload', this.onBeforeLoad, this);
51160             this.store.un('load', this.onLoad, this);
51161             this.store.un('loadexception', this.onLoad, this);
51162         }else{
51163             var um = this.el.getUpdateManager();
51164             um.un('beforeupdate', this.onBeforeLoad, this);
51165             um.un('update', this.onLoad, this);
51166             um.un('failure', this.onLoad, this);
51167         }
51168     }
51169 };/*
51170  * Based on:
51171  * Ext JS Library 1.1.1
51172  * Copyright(c) 2006-2007, Ext JS, LLC.
51173  *
51174  * Originally Released Under LGPL - original licence link has changed is not relivant.
51175  *
51176  * Fork - LGPL
51177  * <script type="text/javascript">
51178  */
51179 Roo.XTemplate = function(){
51180     Roo.XTemplate.superclass.constructor.apply(this, arguments);
51181     var s = this.html;
51182
51183     s = ['<tpl>', s, '</tpl>'].join('');
51184
51185     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
51186
51187     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
51188     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
51189     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
51190     var m, id = 0;
51191     var tpls = [];
51192
51193     while(m = s.match(re)){
51194        var m2 = m[0].match(nameRe);
51195        var m3 = m[0].match(ifRe);
51196        var m4 = m[0].match(execRe);
51197        var exp = null, fn = null, exec = null;
51198        var name = m2 && m2[1] ? m2[1] : '';
51199        if(m3){
51200            exp = m3 && m3[1] ? m3[1] : null;
51201            if(exp){
51202                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
51203            }
51204        }
51205        if(m4){
51206            exp = m4 && m4[1] ? m4[1] : null;
51207            if(exp){
51208                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
51209            }
51210        }
51211        if(name){
51212            switch(name){
51213                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
51214                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
51215                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
51216            }
51217        }
51218        tpls.push({
51219             id: id,
51220             target: name,
51221             exec: exec,
51222             test: fn,
51223             body: m[1]||''
51224         });
51225        s = s.replace(m[0], '{xtpl'+ id + '}');
51226        ++id;
51227     }
51228     for(var i = tpls.length-1; i >= 0; --i){
51229         this.compileTpl(tpls[i]);
51230     }
51231     this.master = tpls[tpls.length-1];
51232     this.tpls = tpls;
51233 };
51234 Roo.extend(Roo.XTemplate, Roo.Template, {
51235
51236     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
51237
51238     applySubTemplate : function(id, values, parent){
51239         var t = this.tpls[id];
51240         if(t.test && !t.test.call(this, values, parent)){
51241             return '';
51242         }
51243         if(t.exec && t.exec.call(this, values, parent)){
51244             return '';
51245         }
51246         var vs = t.target ? t.target.call(this, values, parent) : values;
51247         parent = t.target ? values : parent;
51248         if(t.target && vs instanceof Array){
51249             var buf = [];
51250             for(var i = 0, len = vs.length; i < len; i++){
51251                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
51252             }
51253             return buf.join('');
51254         }
51255         return t.compiled.call(this, vs, parent);
51256     },
51257
51258     compileTpl : function(tpl){
51259         var fm = Roo.util.Format;
51260         var useF = this.disableFormats !== true;
51261         var sep = Roo.isGecko ? "+" : ",";
51262         var fn = function(m, name, format, args){
51263             if(name.substr(0, 4) == 'xtpl'){
51264                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
51265             }
51266             var v;
51267             if(name.indexOf('.') != -1){
51268                 v = name;
51269             }else{
51270                 v = "values['" + name + "']";
51271             }
51272             if(format && useF){
51273                 args = args ? ',' + args : "";
51274                 if(format.substr(0, 5) != "this."){
51275                     format = "fm." + format + '(';
51276                 }else{
51277                     format = 'this.call("'+ format.substr(5) + '", ';
51278                     args = ", values";
51279                 }
51280             }else{
51281                 args= ''; format = "("+v+" === undefined ? '' : ";
51282             }
51283             return "'"+ sep + format + v + args + ")"+sep+"'";
51284         };
51285         var body;
51286         // branched to use + in gecko and [].join() in others
51287         if(Roo.isGecko){
51288             body = "tpl.compiled = function(values, parent){ return '" +
51289                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
51290                     "';};";
51291         }else{
51292             body = ["tpl.compiled = function(values, parent){ return ['"];
51293             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
51294             body.push("'].join('');};");
51295             body = body.join('');
51296         }
51297         /** eval:var:zzzzzzz */
51298         eval(body);
51299         return this;
51300     },
51301
51302     applyTemplate : function(values){
51303         return this.master.compiled.call(this, values, {});
51304         var s = this.subs;
51305     },
51306
51307     apply : function(){
51308         return this.applyTemplate.apply(this, arguments);
51309     },
51310
51311     compile : function(){return this;}
51312 });
51313
51314 Roo.XTemplate.from = function(el){
51315     el = Roo.getDom(el);
51316     return new Roo.XTemplate(el.value || el.innerHTML);
51317 };/*
51318  * Original code for Roojs - LGPL
51319  * <script type="text/javascript">
51320  */
51321  
51322 /**
51323  * @class Roo.XComponent
51324  * A delayed Element creator...
51325  * Or a way to group chunks of interface together.
51326  * 
51327  * Mypart.xyx = new Roo.XComponent({
51328
51329     parent : 'Mypart.xyz', // empty == document.element.!!
51330     order : '001',
51331     name : 'xxxx'
51332     region : 'xxxx'
51333     disabled : function() {} 
51334      
51335     tree : function() { // return an tree of xtype declared components
51336         var MODULE = this;
51337         return 
51338         {
51339             xtype : 'NestedLayoutPanel',
51340             // technicall
51341         }
51342      ]
51343  *})
51344  *
51345  *
51346  * It can be used to build a big heiracy, with parent etc.
51347  * or you can just use this to render a single compoent to a dom element
51348  * MYPART.render(Roo.Element | String(id) | dom_element )
51349  * 
51350  * @extends Roo.util.Observable
51351  * @constructor
51352  * @param cfg {Object} configuration of component
51353  * 
51354  */
51355 Roo.XComponent = function(cfg) {
51356     Roo.apply(this, cfg);
51357     this.addEvents({ 
51358         /**
51359              * @event built
51360              * Fires when this the componnt is built
51361              * @param {Roo.XComponent} c the component
51362              */
51363         'built' : true,
51364         /**
51365              * @event buildcomplete
51366              * Fires on the top level element when all elements have been built
51367              * @param {Roo.XComponent} c the top level component.
51368          */
51369         'buildcomplete' : true
51370         
51371     });
51372     this.region = this.region || 'center'; // default..
51373     Roo.XComponent.register(this);
51374     this.modules = false;
51375     this.el = false; // where the layout goes..
51376     
51377     
51378 }
51379 Roo.extend(Roo.XComponent, Roo.util.Observable, {
51380     /**
51381      * @property el
51382      * The created element (with Roo.factory())
51383      * @type {Roo.Layout}
51384      */
51385     el  : false,
51386     
51387     /**
51388      * @property el
51389      * for BC  - use el in new code
51390      * @type {Roo.Layout}
51391      */
51392     panel : false,
51393     
51394     /**
51395      * @property layout
51396      * for BC  - use el in new code
51397      * @type {Roo.Layout}
51398      */
51399     layout : false,
51400     
51401      /**
51402      * @cfg {Function|boolean} disabled
51403      * If this module is disabled by some rule, return true from the funtion
51404      */
51405     disabled : false,
51406     
51407     /**
51408      * @cfg {String} parent 
51409      * Name of parent element which it get xtype added to..
51410      */
51411     parent: false,
51412     
51413     /**
51414      * @cfg {String} order
51415      * Used to set the order in which elements are created (usefull for multiple tabs)
51416      */
51417     
51418     order : false,
51419     /**
51420      * @cfg {String} name
51421      * String to display while loading.
51422      */
51423     name : false,
51424     /**
51425      * @cfg {String} region
51426      * Region to render component to (defaults to center)
51427      */
51428     region : 'center',
51429     
51430     /**
51431      * @cfg {Array} items
51432      * A single item array - the first element is the root of the tree..
51433      * It's done this way to stay compatible with the Xtype system...
51434      */
51435     items : false,
51436     
51437     
51438      /**
51439      * render
51440      * render element to dom or tree
51441      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
51442      */
51443     
51444     render : function(el)
51445     {
51446         
51447         el = el || false;
51448         var hp = this.parent ? 1 : 0;
51449         
51450         if (!el && typeof(this.parent) == 'string' && this.parent[0] == '#') {
51451             // if parent is a '#.....' string, then let's use that..
51452             var ename = this.parent.substr(1)
51453             this.parent = false;
51454             el = Roo.get(ename);
51455             if (!el) {
51456                 Roo.log("Warning - element can not be found :#" + ename );
51457                 return;
51458             }
51459         }
51460         
51461         
51462         if (!this.parent) {
51463             
51464             el = el ? Roo.get(el) : false;
51465             
51466             // it's a top level one..
51467             this.parent =  {
51468                 el : new Roo.BorderLayout(el || document.body, {
51469                 
51470                      center: {
51471                          titlebar: false,
51472                          autoScroll:false,
51473                          closeOnTab: true,
51474                          tabPosition: 'top',
51475                           //resizeTabs: true,
51476                          alwaysShowTabs: el && hp? false :  true,
51477                          hideTabs: el || !hp ? true :  false,
51478                          minTabWidth: 140
51479                      }
51480                  })
51481             }
51482         }
51483         
51484         
51485             
51486         var tree = this.tree();
51487         tree.region = tree.region || this.region;
51488         this.el = this.parent.el.addxtype(tree);
51489         this.fireEvent('built', this);
51490         
51491         this.panel = this.el;
51492         this.layout = this.panel.layout;    
51493          
51494     }
51495     
51496 });
51497
51498 Roo.apply(Roo.XComponent, {
51499     
51500     /**
51501      * @property  buildCompleted
51502      * True when the builder has completed building the interface.
51503      * @type Boolean
51504      */
51505     buildCompleted : false,
51506      
51507     /**
51508      * @property  topModule
51509      * the upper most module - uses document.element as it's constructor.
51510      * @type Object
51511      */
51512      
51513     topModule  : false,
51514       
51515     /**
51516      * @property  modules
51517      * array of modules to be created by registration system.
51518      * @type {Array} of Roo.XComponent
51519      */
51520     
51521     modules : [],
51522     /**
51523      * @property  elmodules
51524      * array of modules to be created by which use #ID 
51525      * @type {Array} of Roo.XComponent
51526      */
51527      
51528     elmodules : [],
51529
51530     
51531     /**
51532      * Register components to be built later.
51533      *
51534      * This solves the following issues
51535      * - Building is not done on page load, but after an authentication process has occured.
51536      * - Interface elements are registered on page load
51537      * - Parent Interface elements may not be loaded before child, so this handles that..
51538      * 
51539      *
51540      * example:
51541      * 
51542      * MyApp.register({
51543           order : '000001',
51544           module : 'Pman.Tab.projectMgr',
51545           region : 'center',
51546           parent : 'Pman.layout',
51547           disabled : false,  // or use a function..
51548         })
51549      
51550      * * @param {Object} details about module
51551      */
51552     register : function(obj) {
51553         this.modules.push(obj);
51554          
51555     },
51556     /**
51557      * convert a string to an object..
51558      * eg. 'AAA.BBB' -> finds AAA.BBB
51559
51560      */
51561     
51562     toObject : function(str)
51563     {
51564         if (!str || typeof(str) == 'object') {
51565             return str;
51566         }
51567         if (str[0]=='#') {
51568             return str;
51569         }
51570
51571         var ar = str.split('.');
51572         var rt, o;
51573         rt = ar.shift();
51574             /** eval:var:o */
51575         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51576         if (o === false) {
51577             throw "Module not found : " + str;
51578         }
51579         Roo.each(ar, function(e) {
51580             if (typeof(o[e]) == 'undefined') {
51581                 throw "Module not found : " + str;
51582             }
51583             o = o[e];
51584         });
51585         
51586         return o;
51587         
51588     },
51589     
51590     
51591     /**
51592      * move modules into their correct place in the tree..
51593      * 
51594      */
51595     preBuild : function ()
51596     {
51597         var _t = this;
51598         Roo.each(this.modules , function (obj)
51599         {
51600             var opar = obj.parent;
51601             obj.parent = this.toObject(opar);
51602             
51603             if (!obj.parent) {
51604                 this.topModule = obj;
51605                 return;
51606             }
51607             if (typeof(obj.parent) == 'string') {
51608                 this.elmodules.push(obj);
51609                 return;
51610             }
51611             if (obj.parent.constructor != Roo.XComponent) {
51612                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
51613             }
51614             if (!obj.parent.modules) {
51615                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51616                     function(o) { return o.order + '' }
51617                 );
51618             }
51619             
51620             obj.parent.modules.add(obj);
51621         }, this);
51622     },
51623     
51624      /**
51625      * make a list of modules to build.
51626      * @return {Array} list of modules. 
51627      */ 
51628     
51629     buildOrder : function()
51630     {
51631         var _this = this;
51632         var cmp = function(a,b) {   
51633             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51634         };
51635         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
51636             throw "No top level modules to build";
51637         }
51638         
51639         // make a flat list in order of modules to build.
51640         var mods = this.topModule ? [ this.topModule ] : [];
51641         Roo.each(this.elmodules,function(e) { mods.push(e) });
51642
51643         
51644         // add modules to their parents..
51645         var addMod = function(m) {
51646            // Roo.debug && Roo.log(m.modKey);
51647             
51648             mods.push(m);
51649             if (m.modules) {
51650                 m.modules.keySort('ASC',  cmp );
51651                 m.modules.each(addMod);
51652             }
51653             // not sure if this is used any more..
51654             if (m.finalize) {
51655                 m.finalize.name = m.name + " (clean up) ";
51656                 mods.push(m.finalize);
51657             }
51658             
51659         }
51660         if (this.topModule) { 
51661             this.topModule.modules.keySort('ASC',  cmp );
51662             this.topModule.modules.each(addMod);
51663         }
51664         return mods;
51665     },
51666     
51667      /**
51668      * Build the registered modules.
51669      * @param {Object} parent element.
51670      * @param {Function} optional method to call after module has been added.
51671      * 
51672      */ 
51673    
51674     build : function() 
51675     {
51676         
51677         this.preBuild();
51678         var mods = this.buildOrder();
51679       
51680         //this.allmods = mods;
51681         //Roo.debug && Roo.log(mods);
51682         //return;
51683         if (!mods.length) { // should not happen
51684             throw "NO modules!!!";
51685         }
51686         
51687         
51688         
51689         // flash it up as modal - so we store the mask!?
51690         Roo.MessageBox.show({ title: 'loading' });
51691         Roo.MessageBox.show({
51692            title: "Please wait...",
51693            msg: "Building Interface...",
51694            width:450,
51695            progress:true,
51696            closable:false,
51697            modal: false
51698           
51699         });
51700         var total = mods.length;
51701         
51702         var _this = this;
51703         var progressRun = function() {
51704             if (!mods.length) {
51705                 Roo.debug && Roo.log('hide?');
51706                 Roo.MessageBox.hide();
51707                 if (_this.topModule) { 
51708                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
51709                 }
51710                 // THE END...
51711                 return false;   
51712             }
51713             
51714             var m = mods.shift();
51715             
51716             
51717             Roo.debug && Roo.log(m);
51718             // not sure if this is supported any more.. - modules that are are just function
51719             if (typeof(m) == 'function') { 
51720                 m.call(this);
51721                 return progressRun.defer(10, _this);
51722             } 
51723             
51724             
51725             
51726             Roo.MessageBox.updateProgress(
51727                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51728                     " of " + total + 
51729                     (m.name ? (' - ' + m.name) : '')
51730                     );
51731             
51732          
51733             // is the module disabled?
51734             var disabled = (typeof(m.disabled) == 'function') ?
51735                 m.disabled.call(m.module.disabled) : m.disabled;    
51736             
51737             
51738             if (disabled) {
51739                 return progressRun(); // we do not update the display!
51740             }
51741             
51742             // now build 
51743             
51744             m.render();
51745             // it's 10 on top level, and 1 on others??? why...
51746             return progressRun.defer(10, _this);
51747              
51748         }
51749         progressRun.defer(1, _this);
51750      
51751         
51752         
51753     }
51754     
51755      
51756    
51757     
51758     
51759 });
51760  //<script type="text/javascript">
51761
51762
51763 /**
51764  * @class Roo.Login
51765  * @extends Roo.LayoutDialog
51766  * A generic Login Dialog..... - only one needed in theory!?!?
51767  *
51768  * Fires XComponent builder on success...
51769  * 
51770  * Sends 
51771  *    username,password, lang = for login actions.
51772  *    check = 1 for periodic checking that sesion is valid.
51773  *    passwordRequest = email request password
51774  *    logout = 1 = to logout
51775  * 
51776  * Affects: (this id="????" elements)
51777  *   loading  (removed) (used to indicate application is loading)
51778  *   loading-mask (hides) (used to hide application when it's building loading)
51779  *   
51780  * 
51781  * Usage: 
51782  *    
51783  * 
51784  * Myapp.login = Roo.Login({
51785      url: xxxx,
51786    
51787      realm : 'Myapp', 
51788      
51789      
51790      method : 'POST',
51791      
51792      
51793      * 
51794  })
51795  * 
51796  * 
51797  * 
51798  **/
51799  
51800 Roo.Login = function(cfg)
51801 {
51802     this.addEvents({
51803         'refreshed' : true
51804     });
51805     
51806     Roo.apply(this,cfg);
51807     
51808     Roo.onReady(function() {
51809         this.onLoad();
51810     }, this);
51811     // call parent..
51812     
51813    
51814     Roo.Login.superclass.constructor.call(this, this);
51815     //this.addxtype(this.items[0]);
51816     
51817     
51818 }
51819
51820
51821 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51822     
51823     /**
51824      * @cfg {String} method
51825      * Method used to query for login details.
51826      */
51827     
51828     method : 'POST',
51829     /**
51830      * @cfg {String} url
51831      * URL to query login data. - eg. baseURL + '/Login.php'
51832      */
51833     url : '',
51834     
51835     /**
51836      * @property user
51837      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51838      * @type {Object} 
51839      */
51840     user : false,
51841     /**
51842      * @property checkFails
51843      * Number of times we have attempted to get authentication check, and failed.
51844      * @type {Number} 
51845      */
51846     checkFails : 0,
51847       /**
51848      * @property intervalID
51849      * The window interval that does the constant login checking.
51850      * @type {Number} 
51851      */
51852     intervalID : 0,
51853     
51854     
51855     onLoad : function() // called on page load...
51856     {
51857         // load 
51858          
51859         if (Roo.get('loading')) { // clear any loading indicator..
51860             Roo.get('loading').remove();
51861         }
51862         
51863         //this.switchLang('en'); // set the language to english..
51864        
51865         this.check({
51866             success:  function(response, opts)  {  // check successfull...
51867             
51868                 var res = this.processResponse(response);
51869                 this.checkFails =0;
51870                 if (!res.success) { // error!
51871                     this.checkFails = 5;
51872                     //console.log('call failure');
51873                     return this.failure(response,opts);
51874                 }
51875                 
51876                 if (!res.data.id) { // id=0 == login failure.
51877                     return this.show();
51878                 }
51879                 
51880                               
51881                         //console.log(success);
51882                 this.fillAuth(res.data);   
51883                 this.checkFails =0;
51884                 Roo.XComponent.build();
51885             },
51886             failure : this.show
51887         });
51888         
51889     }, 
51890     
51891     
51892     check: function(cfg) // called every so often to refresh cookie etc..
51893     {
51894         if (cfg.again) { // could be undefined..
51895             this.checkFails++;
51896         } else {
51897             this.checkFails = 0;
51898         }
51899         var _this = this;
51900         if (this.sending) {
51901             if ( this.checkFails > 4) {
51902                 Roo.MessageBox.alert("Error",  
51903                     "Error getting authentication status. - try reloading, or wait a while", function() {
51904                         _this.sending = false;
51905                     }); 
51906                 return;
51907             }
51908             cfg.again = true;
51909             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51910             return;
51911         }
51912         this.sending = true;
51913         
51914         Roo.Ajax.request({  
51915             url: this.url,
51916             params: {
51917                 getAuthUser: true
51918             },  
51919             method: this.method,
51920             success:  cfg.success || this.success,
51921             failure : cfg.failure || this.failure,
51922             scope : this,
51923             callCfg : cfg
51924               
51925         });  
51926     }, 
51927     
51928     
51929     logout: function()
51930     {
51931         window.onbeforeunload = function() { }; // false does not work for IE..
51932         this.user = false;
51933         var _this = this;
51934         
51935         Roo.Ajax.request({  
51936             url: this.url,
51937             params: {
51938                 logout: 1
51939             },  
51940             method: 'GET',
51941             failure : function() {
51942                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51943                     document.location = document.location.toString() + '?ts=' + Math.random();
51944                 });
51945                 
51946             },
51947             success : function() {
51948                 _this.user = false;
51949                 this.checkFails =0;
51950                 // fixme..
51951                 document.location = document.location.toString() + '?ts=' + Math.random();
51952             }
51953               
51954               
51955         }); 
51956     },
51957     
51958     processResponse : function (response)
51959     {
51960         var res = '';
51961         try {
51962             res = Roo.decode(response.responseText);
51963             // oops...
51964             if (typeof(res) != 'object') {
51965                 res = { success : false, errorMsg : res, errors : true };
51966             }
51967             if (typeof(res.success) == 'undefined') {
51968                 res.success = false;
51969             }
51970             
51971         } catch(e) {
51972             res = { success : false,  errorMsg : response.responseText, errors : true };
51973         }
51974         return res;
51975     },
51976     
51977     success : function(response, opts)  // check successfull...
51978     {  
51979         this.sending = false;
51980         var res = this.processResponse(response);
51981         if (!res.success) {
51982             return this.failure(response, opts);
51983         }
51984         if (!res.data || !res.data.id) {
51985             return this.failure(response,opts);
51986         }
51987         //console.log(res);
51988         this.fillAuth(res.data);
51989         
51990         this.checkFails =0;
51991         
51992     },
51993     
51994     
51995     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51996     {
51997         this.authUser = -1;
51998         this.sending = false;
51999         var res = this.processResponse(response);
52000         //console.log(res);
52001         if ( this.checkFails > 2) {
52002         
52003             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52004                 "Error getting authentication status. - try reloading"); 
52005             return;
52006         }
52007         opts.callCfg.again = true;
52008         this.check.defer(1000, this, [ opts.callCfg ]);
52009         return;  
52010     },
52011     
52012     
52013     
52014     fillAuth: function(au) {
52015         this.startAuthCheck();
52016         this.authUserId = au.id;
52017         this.authUser = au;
52018         this.lastChecked = new Date();
52019         this.fireEvent('refreshed', au);
52020         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52021         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52022         au.lang = au.lang || 'en';
52023         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52024         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52025         this.switchLang(au.lang );
52026         
52027      
52028         // open system... - -on setyp..
52029         if (this.authUserId  < 0) {
52030             Roo.MessageBox.alert("Warning", 
52031                 "This is an open system - please set up a admin user with a password.");  
52032         }
52033          
52034         //Pman.onload(); // which should do nothing if it's a re-auth result...
52035         
52036              
52037     },
52038     
52039     startAuthCheck : function() // starter for timeout checking..
52040     {
52041         if (this.intervalID) { // timer already in place...
52042             return false;
52043         }
52044         var _this = this;
52045         this.intervalID =  window.setInterval(function() {
52046               _this.check(false);
52047             }, 120000); // every 120 secs = 2mins..
52048         
52049         
52050     },
52051          
52052     
52053     switchLang : function (lang) 
52054     {
52055         _T = typeof(_T) == 'undefined' ? false : _T;
52056           if (!_T || !lang.length) {
52057             return;
52058         }
52059         
52060         if (!_T && lang != 'en') {
52061             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52062             return;
52063         }
52064         
52065         if (typeof(_T.en) == 'undefined') {
52066             _T.en = {};
52067             Roo.apply(_T.en, _T);
52068         }
52069         
52070         if (typeof(_T[lang]) == 'undefined') {
52071             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52072             return;
52073         }
52074         
52075         
52076         Roo.apply(_T, _T[lang]);
52077         // just need to set the text values for everything...
52078         var _this = this;
52079         /* this will not work ...
52080         if (this.form) { 
52081             
52082                
52083             function formLabel(name, val) {
52084                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52085             }
52086             
52087             formLabel('password', "Password"+':');
52088             formLabel('username', "Email Address"+':');
52089             formLabel('lang', "Language"+':');
52090             this.dialog.setTitle("Login");
52091             this.dialog.buttons[0].setText("Forgot Password");
52092             this.dialog.buttons[1].setText("Login");
52093         }
52094         */
52095         
52096         
52097     },
52098     
52099     
52100     title: "Login",
52101     modal: true,
52102     width:  350,
52103     //height: 230,
52104     height: 180,
52105     shadow: true,
52106     minWidth:200,
52107     minHeight:180,
52108     //proxyDrag: true,
52109     closable: false,
52110     draggable: false,
52111     collapsible: false,
52112     resizable: false,
52113     center: {  // needed??
52114         autoScroll:false,
52115         titlebar: false,
52116        // tabPosition: 'top',
52117         hideTabs: true,
52118         closeOnTab: true,
52119         alwaysShowTabs: false
52120     } ,
52121     listeners : {
52122         
52123         show  : function(dlg)
52124         {
52125             //console.log(this);
52126             this.form = this.layout.getRegion('center').activePanel.form;
52127             this.form.dialog = dlg;
52128             this.buttons[0].form = this.form;
52129             this.buttons[0].dialog = dlg;
52130             this.buttons[1].form = this.form;
52131             this.buttons[1].dialog = dlg;
52132            
52133            //this.resizeToLogo.defer(1000,this);
52134             // this is all related to resizing for logos..
52135             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52136            //// if (!sz) {
52137              //   this.resizeToLogo.defer(1000,this);
52138              //   return;
52139            // }
52140             //var w = Ext.lib.Dom.getViewWidth() - 100;
52141             //var h = Ext.lib.Dom.getViewHeight() - 100;
52142             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52143             //this.center();
52144             if (this.disabled) {
52145                 this.hide();
52146                 return;
52147             }
52148             
52149             if (this.user.id < 0) { // used for inital setup situations.
52150                 return;
52151             }
52152             
52153             if (this.intervalID) {
52154                 // remove the timer
52155                 window.clearInterval(this.intervalID);
52156                 this.intervalID = false;
52157             }
52158             
52159             
52160             if (Roo.get('loading')) {
52161                 Roo.get('loading').remove();
52162             }
52163             if (Roo.get('loading-mask')) {
52164                 Roo.get('loading-mask').hide();
52165             }
52166             
52167             //incomming._node = tnode;
52168             this.form.reset();
52169             //this.dialog.modal = !modal;
52170             //this.dialog.show();
52171             this.el.unmask(); 
52172             
52173             
52174             this.form.setValues({
52175                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
52176                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
52177             });
52178             
52179             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
52180             if (this.form.findField('username').getValue().length > 0 ){
52181                 this.form.findField('password').focus();
52182             } else {
52183                this.form.findField('username').focus();
52184             }
52185     
52186         }
52187     },
52188     items : [
52189          {
52190        
52191             xtype : 'ContentPanel',
52192             xns : Roo,
52193             region: 'center',
52194             fitToFrame : true,
52195             
52196             items : [
52197     
52198                 {
52199                
52200                     xtype : 'Form',
52201                     xns : Roo.form,
52202                     labelWidth: 100,
52203                     style : 'margin: 10px;',
52204                     
52205                     listeners : {
52206                         actionfailed : function(f, act) {
52207                             // form can return { errors: .... }
52208                                 
52209                             //act.result.errors // invalid form element list...
52210                             //act.result.errorMsg// invalid form element list...
52211                             
52212                             this.dialog.el.unmask();
52213                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
52214                                         "Login failed - communication error - try again.");
52215                                       
52216                         },
52217                         actioncomplete: function(re, act) {
52218                              
52219                             Roo.state.Manager.set(
52220                                 this.dialog.realm + '.username',  
52221                                     this.findField('username').getValue()
52222                             );
52223                             Roo.state.Manager.set(
52224                                 this.dialog.realm + '.lang',  
52225                                 this.findField('lang').getValue() 
52226                             );
52227                             
52228                             this.dialog.fillAuth(act.result.data);
52229                               
52230                             this.dialog.hide();
52231                             
52232                             if (Roo.get('loading-mask')) {
52233                                 Roo.get('loading-mask').show();
52234                             }
52235                             Roo.XComponent.build();
52236                             
52237                              
52238                             
52239                         }
52240                     },
52241                     items : [
52242                         {
52243                             xtype : 'TextField',
52244                             xns : Roo.form,
52245                             fieldLabel: "Email Address",
52246                             name: 'username',
52247                             width:200,
52248                             autoCreate : {tag: "input", type: "text", size: "20"}
52249                         },
52250                         {
52251                             xtype : 'TextField',
52252                             xns : Roo.form,
52253                             fieldLabel: "Password",
52254                             inputType: 'password',
52255                             name: 'password',
52256                             width:200,
52257                             autoCreate : {tag: "input", type: "text", size: "20"},
52258                             listeners : {
52259                                 specialkey : function(e,ev) {
52260                                     if (ev.keyCode == 13) {
52261                                         this.form.dialog.el.mask("Logging in");
52262                                         this.form.doAction('submit', {
52263                                             url: this.form.dialog.url,
52264                                             method: this.form.dialog.method
52265                                         });
52266                                     }
52267                                 }
52268                             }  
52269                         },
52270                         {
52271                             xtype : 'ComboBox',
52272                             xns : Roo.form,
52273                             fieldLabel: "Language",
52274                             name : 'langdisp',
52275                             store: {
52276                                 xtype : 'SimpleStore',
52277                                 fields: ['lang', 'ldisp'],
52278                                 data : [
52279                                     [ 'en', 'English' ],
52280                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
52281                                     [ 'zh_CN', '\u7C21\u4E2D' ]
52282                                 ]
52283                             },
52284                             
52285                             valueField : 'lang',
52286                             hiddenName:  'lang',
52287                             width: 200,
52288                             displayField:'ldisp',
52289                             typeAhead: false,
52290                             editable: false,
52291                             mode: 'local',
52292                             triggerAction: 'all',
52293                             emptyText:'Select a Language...',
52294                             selectOnFocus:true,
52295                             listeners : {
52296                                 select :  function(cb, rec, ix) {
52297                                     this.form.switchLang(rec.data.lang);
52298                                 }
52299                             }
52300                         
52301                         }
52302                     ]
52303                 }
52304                   
52305                 
52306             ]
52307         }
52308     ],
52309     buttons : [
52310         {
52311             xtype : 'Button',
52312             xns : 'Roo',
52313             text : "Forgot Password",
52314             listeners : {
52315                 click : function() {
52316                     //console.log(this);
52317                     var n = this.form.findField('username').getValue();
52318                     if (!n.length) {
52319                         Roo.MessageBox.alert("Error", "Fill in your email address");
52320                         return;
52321                     }
52322                     Roo.Ajax.request({
52323                         url: this.dialog.url,
52324                         params: {
52325                             passwordRequest: n
52326                         },
52327                         method: this.dialog.method,
52328                         success:  function(response, opts)  {  // check successfull...
52329                         
52330                             var res = this.dialog.processResponse(response);
52331                             if (!res.success) { // error!
52332                                Roo.MessageBox.alert("Error" ,
52333                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
52334                                return;
52335                             }
52336                             Roo.MessageBox.alert("Notice" ,
52337                                 "Please check you email for the Password Reset message");
52338                         },
52339                         failure : function() {
52340                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
52341                         }
52342                         
52343                     });
52344                 }
52345             }
52346         },
52347         {
52348             xtype : 'Button',
52349             xns : 'Roo',
52350             text : "Login",
52351             listeners : {
52352                 
52353                 click : function () {
52354                         
52355                     this.dialog.el.mask("Logging in");
52356                     this.form.doAction('submit', {
52357                             url: this.dialog.url,
52358                             method: this.dialog.method
52359                     });
52360                 }
52361             }
52362         }
52363     ]
52364   
52365   
52366 })
52367  
52368
52369
52370