roojs-debug.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618
619         /**
620          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
621          * you may want to set this to true.
622          * @type Boolean
623          */
624         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
625         
626         
627                 
628         /**
629          * Selects a single element as a Roo Element
630          * This is about as close as you can get to jQuery's $('do crazy stuff')
631          * @param {String} selector The selector/xpath query
632          * @param {Node} root (optional) The start of the query (defaults to document).
633          * @return {Roo.Element}
634          */
635         selectNode : function(selector, root) 
636         {
637             var node = Roo.DomQuery.selectNode(selector,root);
638             return node ? Roo.get(node) : new Roo.Element(false);
639         }
640         
641     });
642
643
644 })();
645
646 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
647                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
648 /*
649  * Based on:
650  * Ext JS Library 1.1.1
651  * Copyright(c) 2006-2007, Ext JS, LLC.
652  *
653  * Originally Released Under LGPL - original licence link has changed is not relivant.
654  *
655  * Fork - LGPL
656  * <script type="text/javascript">
657  */
658
659 (function() {    
660     // wrappedn so fnCleanup is not in global scope...
661     if(Roo.isIE) {
662         function fnCleanUp() {
663             var p = Function.prototype;
664             delete p.createSequence;
665             delete p.defer;
666             delete p.createDelegate;
667             delete p.createCallback;
668             delete p.createInterceptor;
669
670             window.detachEvent("onunload", fnCleanUp);
671         }
672         window.attachEvent("onunload", fnCleanUp);
673     }
674 })();
675
676
677 /**
678  * @class Function
679  * These functions are available on every Function object (any JavaScript function).
680  */
681 Roo.apply(Function.prototype, {
682      /**
683      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
684      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
685      * Will create a function that is bound to those 2 args.
686      * @return {Function} The new function
687     */
688     createCallback : function(/*args...*/){
689         // make args available, in function below
690         var args = arguments;
691         var method = this;
692         return function() {
693             return method.apply(window, args);
694         };
695     },
696
697     /**
698      * Creates a delegate (callback) that sets the scope to obj.
699      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
700      * Will create a function that is automatically scoped to this.
701      * @param {Object} obj (optional) The object for which the scope is set
702      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
703      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
704      *                                             if a number the args are inserted at the specified position
705      * @return {Function} The new function
706      */
707     createDelegate : function(obj, args, appendArgs){
708         var method = this;
709         return function() {
710             var callArgs = args || arguments;
711             if(appendArgs === true){
712                 callArgs = Array.prototype.slice.call(arguments, 0);
713                 callArgs = callArgs.concat(args);
714             }else if(typeof appendArgs == "number"){
715                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
716                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
717                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
718             }
719             return method.apply(obj || window, callArgs);
720         };
721     },
722
723     /**
724      * Calls this function after the number of millseconds specified.
725      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
726      * @param {Object} obj (optional) The object for which the scope is set
727      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
728      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
729      *                                             if a number the args are inserted at the specified position
730      * @return {Number} The timeout id that can be used with clearTimeout
731      */
732     defer : function(millis, obj, args, appendArgs){
733         var fn = this.createDelegate(obj, args, appendArgs);
734         if(millis){
735             return setTimeout(fn, millis);
736         }
737         fn();
738         return 0;
739     },
740     /**
741      * Create a combined function call sequence of the original function + the passed function.
742      * The resulting function returns the results of the original function.
743      * The passed fcn is called with the parameters of the original function
744      * @param {Function} fcn The function to sequence
745      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
746      * @return {Function} The new function
747      */
748     createSequence : function(fcn, scope){
749         if(typeof fcn != "function"){
750             return this;
751         }
752         var method = this;
753         return function() {
754             var retval = method.apply(this || window, arguments);
755             fcn.apply(scope || this || window, arguments);
756             return retval;
757         };
758     },
759
760     /**
761      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
762      * The resulting function returns the results of the original function.
763      * The passed fcn is called with the parameters of the original function.
764      * @addon
765      * @param {Function} fcn The function to call before the original
766      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
767      * @return {Function} The new function
768      */
769     createInterceptor : function(fcn, scope){
770         if(typeof fcn != "function"){
771             return this;
772         }
773         var method = this;
774         return function() {
775             fcn.target = this;
776             fcn.method = method;
777             if(fcn.apply(scope || this || window, arguments) === false){
778                 return;
779             }
780             return method.apply(this || window, arguments);
781         };
782     }
783 });
784 /*
785  * Based on:
786  * Ext JS Library 1.1.1
787  * Copyright(c) 2006-2007, Ext JS, LLC.
788  *
789  * Originally Released Under LGPL - original licence link has changed is not relivant.
790  *
791  * Fork - LGPL
792  * <script type="text/javascript">
793  */
794
795 Roo.applyIf(String, {
796     
797     /** @scope String */
798     
799     /**
800      * Escapes the passed string for ' and \
801      * @param {String} string The string to escape
802      * @return {String} The escaped string
803      * @static
804      */
805     escape : function(string) {
806         return string.replace(/('|\\)/g, "\\$1");
807     },
808
809     /**
810      * Pads the left side of a string with a specified character.  This is especially useful
811      * for normalizing number and date strings.  Example usage:
812      * <pre><code>
813 var s = String.leftPad('123', 5, '0');
814 // s now contains the string: '00123'
815 </code></pre>
816      * @param {String} string The original string
817      * @param {Number} size The total length of the output string
818      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
819      * @return {String} The padded string
820      * @static
821      */
822     leftPad : function (val, size, ch) {
823         var result = new String(val);
824         if(ch === null || ch === undefined || ch === '') {
825             ch = " ";
826         }
827         while (result.length < size) {
828             result = ch + result;
829         }
830         return result;
831     },
832
833     /**
834      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
835      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
836      * <pre><code>
837 var cls = 'my-class', text = 'Some text';
838 var s = String.format('<div class="{0}">{1}</div>', cls, text);
839 // s now contains the string: '<div class="my-class">Some text</div>'
840 </code></pre>
841      * @param {String} string The tokenized string to be formatted
842      * @param {String} value1 The value to replace token {0}
843      * @param {String} value2 Etc...
844      * @return {String} The formatted string
845      * @static
846      */
847     format : function(format){
848         var args = Array.prototype.slice.call(arguments, 1);
849         return format.replace(/\{(\d+)\}/g, function(m, i){
850             return Roo.util.Format.htmlEncode(args[i]);
851         });
852     }
853 });
854
855 /**
856  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
857  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
858  * they are already different, the first value passed in is returned.  Note that this method returns the new value
859  * but does not change the current string.
860  * <pre><code>
861 // alternate sort directions
862 sort = sort.toggle('ASC', 'DESC');
863
864 // instead of conditional logic:
865 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
866 </code></pre>
867  * @param {String} value The value to compare to the current string
868  * @param {String} other The new value to use if the string already equals the first value passed in
869  * @return {String} The new value
870  */
871  
872 String.prototype.toggle = function(value, other){
873     return this == value ? other : value;
874 };/*
875  * Based on:
876  * Ext JS Library 1.1.1
877  * Copyright(c) 2006-2007, Ext JS, LLC.
878  *
879  * Originally Released Under LGPL - original licence link has changed is not relivant.
880  *
881  * Fork - LGPL
882  * <script type="text/javascript">
883  */
884
885  /**
886  * @class Number
887  */
888 Roo.applyIf(Number.prototype, {
889     /**
890      * Checks whether or not the current number is within a desired range.  If the number is already within the
891      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
892      * exceeded.  Note that this method returns the constrained value but does not change the current number.
893      * @param {Number} min The minimum number in the range
894      * @param {Number} max The maximum number in the range
895      * @return {Number} The constrained value if outside the range, otherwise the current value
896      */
897     constrain : function(min, max){
898         return Math.min(Math.max(this, min), max);
899     }
900 });/*
901  * Based on:
902  * Ext JS Library 1.1.1
903  * Copyright(c) 2006-2007, Ext JS, LLC.
904  *
905  * Originally Released Under LGPL - original licence link has changed is not relivant.
906  *
907  * Fork - LGPL
908  * <script type="text/javascript">
909  */
910  /**
911  * @class Array
912  */
913 Roo.applyIf(Array.prototype, {
914     /**
915      * Checks whether or not the specified object exists in the array.
916      * @param {Object} o The object to check for
917      * @return {Number} The index of o in the array (or -1 if it is not found)
918      */
919     indexOf : function(o){
920        for (var i = 0, len = this.length; i < len; i++){
921               if(this[i] == o) return i;
922        }
923            return -1;
924     },
925
926     /**
927      * Removes the specified object from the array.  If the object is not found nothing happens.
928      * @param {Object} o The object to remove
929      */
930     remove : function(o){
931        var index = this.indexOf(o);
932        if(index != -1){
933            this.splice(index, 1);
934        }
935     },
936     /**
937      * Map (JS 1.6 compatibility)
938      * @param {Function} function  to call
939      */
940     map : function(fun )
941     {
942         var len = this.length >>> 0;
943         if (typeof fun != "function")
944             throw new TypeError();
945
946         var res = new Array(len);
947         var thisp = arguments[1];
948         for (var i = 0; i < len; i++)
949         {
950             if (i in this)
951                 res[i] = fun.call(thisp, this[i], i, this);
952         }
953
954         return res;
955     }
956     
957 });
958
959
960  /*
961  * Based on:
962  * Ext JS Library 1.1.1
963  * Copyright(c) 2006-2007, Ext JS, LLC.
964  *
965  * Originally Released Under LGPL - original licence link has changed is not relivant.
966  *
967  * Fork - LGPL
968  * <script type="text/javascript">
969  */
970
971 /**
972  * @class Date
973  *
974  * The date parsing and format syntax is a subset of
975  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
976  * supported will provide results equivalent to their PHP versions.
977  *
978  * Following is the list of all currently supported formats:
979  *<pre>
980 Sample date:
981 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
982
983 Format  Output      Description
984 ------  ----------  --------------------------------------------------------------
985   d      10         Day of the month, 2 digits with leading zeros
986   D      Wed        A textual representation of a day, three letters
987   j      10         Day of the month without leading zeros
988   l      Wednesday  A full textual representation of the day of the week
989   S      th         English ordinal day of month suffix, 2 chars (use with j)
990   w      3          Numeric representation of the day of the week
991   z      9          The julian date, or day of the year (0-365)
992   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
993   F      January    A full textual representation of the month
994   m      01         Numeric representation of a month, with leading zeros
995   M      Jan        Month name abbreviation, three letters
996   n      1          Numeric representation of a month, without leading zeros
997   t      31         Number of days in the given month
998   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
999   Y      2007       A full numeric representation of a year, 4 digits
1000   y      07         A two digit representation of a year
1001   a      pm         Lowercase Ante meridiem and Post meridiem
1002   A      PM         Uppercase Ante meridiem and Post meridiem
1003   g      3          12-hour format of an hour without leading zeros
1004   G      15         24-hour format of an hour without leading zeros
1005   h      03         12-hour format of an hour with leading zeros
1006   H      15         24-hour format of an hour with leading zeros
1007   i      05         Minutes with leading zeros
1008   s      01         Seconds, with leading zeros
1009   O      -0600      Difference to Greenwich time (GMT) in hours
1010   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1011   T      CST        Timezone setting of the machine running the code
1012   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1013 </pre>
1014  *
1015  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1016  * <pre><code>
1017 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1018 document.write(dt.format('Y-m-d'));                         //2007-01-10
1019 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1020 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1021  </code></pre>
1022  *
1023  * Here are some standard date/time patterns that you might find helpful.  They
1024  * are not part of the source of Date.js, but to use them you can simply copy this
1025  * block of code into any script that is included after Date.js and they will also become
1026  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1027  * <pre><code>
1028 Date.patterns = {
1029     ISO8601Long:"Y-m-d H:i:s",
1030     ISO8601Short:"Y-m-d",
1031     ShortDate: "n/j/Y",
1032     LongDate: "l, F d, Y",
1033     FullDateTime: "l, F d, Y g:i:s A",
1034     MonthDay: "F d",
1035     ShortTime: "g:i A",
1036     LongTime: "g:i:s A",
1037     SortableDateTime: "Y-m-d\\TH:i:s",
1038     UniversalSortableDateTime: "Y-m-d H:i:sO",
1039     YearMonth: "F, Y"
1040 };
1041 </code></pre>
1042  *
1043  * Example usage:
1044  * <pre><code>
1045 var dt = new Date();
1046 document.write(dt.format(Date.patterns.ShortDate));
1047  </code></pre>
1048  */
1049
1050 /*
1051  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1052  * They generate precompiled functions from date formats instead of parsing and
1053  * processing the pattern every time you format a date.  These functions are available
1054  * on every Date object (any javascript function).
1055  *
1056  * The original article and download are here:
1057  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1058  *
1059  */
1060  
1061  
1062  // was in core
1063 /**
1064  Returns the number of milliseconds between this date and date
1065  @param {Date} date (optional) Defaults to now
1066  @return {Number} The diff in milliseconds
1067  @member Date getElapsed
1068  */
1069 Date.prototype.getElapsed = function(date) {
1070         return Math.abs((date || new Date()).getTime()-this.getTime());
1071 };
1072 // was in date file..
1073
1074
1075 // private
1076 Date.parseFunctions = {count:0};
1077 // private
1078 Date.parseRegexes = [];
1079 // private
1080 Date.formatFunctions = {count:0};
1081
1082 // private
1083 Date.prototype.dateFormat = function(format) {
1084     if (Date.formatFunctions[format] == null) {
1085         Date.createNewFormat(format);
1086     }
1087     var func = Date.formatFunctions[format];
1088     return this[func]();
1089 };
1090
1091
1092 /**
1093  * Formats a date given the supplied format string
1094  * @param {String} format The format string
1095  * @return {String} The formatted date
1096  * @method
1097  */
1098 Date.prototype.format = Date.prototype.dateFormat;
1099
1100 // private
1101 Date.createNewFormat = function(format) {
1102     var funcName = "format" + Date.formatFunctions.count++;
1103     Date.formatFunctions[format] = funcName;
1104     var code = "Date.prototype." + funcName + " = function(){return ";
1105     var special = false;
1106     var ch = '';
1107     for (var i = 0; i < format.length; ++i) {
1108         ch = format.charAt(i);
1109         if (!special && ch == "\\") {
1110             special = true;
1111         }
1112         else if (special) {
1113             special = false;
1114             code += "'" + String.escape(ch) + "' + ";
1115         }
1116         else {
1117             code += Date.getFormatCode(ch);
1118         }
1119     }
1120     /** eval:var:zzzzzzzzzzzzz */
1121     eval(code.substring(0, code.length - 3) + ";}");
1122 };
1123
1124 // private
1125 Date.getFormatCode = function(character) {
1126     switch (character) {
1127     case "d":
1128         return "String.leftPad(this.getDate(), 2, '0') + ";
1129     case "D":
1130         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1131     case "j":
1132         return "this.getDate() + ";
1133     case "l":
1134         return "Date.dayNames[this.getDay()] + ";
1135     case "S":
1136         return "this.getSuffix() + ";
1137     case "w":
1138         return "this.getDay() + ";
1139     case "z":
1140         return "this.getDayOfYear() + ";
1141     case "W":
1142         return "this.getWeekOfYear() + ";
1143     case "F":
1144         return "Date.monthNames[this.getMonth()] + ";
1145     case "m":
1146         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1147     case "M":
1148         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1149     case "n":
1150         return "(this.getMonth() + 1) + ";
1151     case "t":
1152         return "this.getDaysInMonth() + ";
1153     case "L":
1154         return "(this.isLeapYear() ? 1 : 0) + ";
1155     case "Y":
1156         return "this.getFullYear() + ";
1157     case "y":
1158         return "('' + this.getFullYear()).substring(2, 4) + ";
1159     case "a":
1160         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1161     case "A":
1162         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1163     case "g":
1164         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1165     case "G":
1166         return "this.getHours() + ";
1167     case "h":
1168         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1169     case "H":
1170         return "String.leftPad(this.getHours(), 2, '0') + ";
1171     case "i":
1172         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1173     case "s":
1174         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1175     case "O":
1176         return "this.getGMTOffset() + ";
1177     case "P":
1178         return "this.getGMTColonOffset() + ";
1179     case "T":
1180         return "this.getTimezone() + ";
1181     case "Z":
1182         return "(this.getTimezoneOffset() * -60) + ";
1183     default:
1184         return "'" + String.escape(character) + "' + ";
1185     }
1186 };
1187
1188 /**
1189  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1190  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1191  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1192  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1193  * string or the parse operation will fail.
1194  * Example Usage:
1195 <pre><code>
1196 //dt = Fri May 25 2007 (current date)
1197 var dt = new Date();
1198
1199 //dt = Thu May 25 2006 (today's month/day in 2006)
1200 dt = Date.parseDate("2006", "Y");
1201
1202 //dt = Sun Jan 15 2006 (all date parts specified)
1203 dt = Date.parseDate("2006-1-15", "Y-m-d");
1204
1205 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1206 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1207 </code></pre>
1208  * @param {String} input The unparsed date as a string
1209  * @param {String} format The format the date is in
1210  * @return {Date} The parsed date
1211  * @static
1212  */
1213 Date.parseDate = function(input, format) {
1214     if (Date.parseFunctions[format] == null) {
1215         Date.createParser(format);
1216     }
1217     var func = Date.parseFunctions[format];
1218     return Date[func](input);
1219 };
1220 /**
1221  * @private
1222  */
1223 Date.createParser = function(format) {
1224     var funcName = "parse" + Date.parseFunctions.count++;
1225     var regexNum = Date.parseRegexes.length;
1226     var currentGroup = 1;
1227     Date.parseFunctions[format] = funcName;
1228
1229     var code = "Date." + funcName + " = function(input){\n"
1230         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1231         + "var d = new Date();\n"
1232         + "y = d.getFullYear();\n"
1233         + "m = d.getMonth();\n"
1234         + "d = d.getDate();\n"
1235         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1236         + "if (results && results.length > 0) {";
1237     var regex = "";
1238
1239     var special = false;
1240     var ch = '';
1241     for (var i = 0; i < format.length; ++i) {
1242         ch = format.charAt(i);
1243         if (!special && ch == "\\") {
1244             special = true;
1245         }
1246         else if (special) {
1247             special = false;
1248             regex += String.escape(ch);
1249         }
1250         else {
1251             var obj = Date.formatCodeToRegex(ch, currentGroup);
1252             currentGroup += obj.g;
1253             regex += obj.s;
1254             if (obj.g && obj.c) {
1255                 code += obj.c;
1256             }
1257         }
1258     }
1259
1260     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1261         + "{v = new Date(y, m, d, h, i, s);}\n"
1262         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1263         + "{v = new Date(y, m, d, h, i);}\n"
1264         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1265         + "{v = new Date(y, m, d, h);}\n"
1266         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1267         + "{v = new Date(y, m, d);}\n"
1268         + "else if (y >= 0 && m >= 0)\n"
1269         + "{v = new Date(y, m);}\n"
1270         + "else if (y >= 0)\n"
1271         + "{v = new Date(y);}\n"
1272         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1273         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1274         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1275         + ";}";
1276
1277     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1278     /** eval:var:zzzzzzzzzzzzz */
1279     eval(code);
1280 };
1281
1282 // private
1283 Date.formatCodeToRegex = function(character, currentGroup) {
1284     switch (character) {
1285     case "D":
1286         return {g:0,
1287         c:null,
1288         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1289     case "j":
1290         return {g:1,
1291             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1292             s:"(\\d{1,2})"}; // day of month without leading zeroes
1293     case "d":
1294         return {g:1,
1295             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1296             s:"(\\d{2})"}; // day of month with leading zeroes
1297     case "l":
1298         return {g:0,
1299             c:null,
1300             s:"(?:" + Date.dayNames.join("|") + ")"};
1301     case "S":
1302         return {g:0,
1303             c:null,
1304             s:"(?:st|nd|rd|th)"};
1305     case "w":
1306         return {g:0,
1307             c:null,
1308             s:"\\d"};
1309     case "z":
1310         return {g:0,
1311             c:null,
1312             s:"(?:\\d{1,3})"};
1313     case "W":
1314         return {g:0,
1315             c:null,
1316             s:"(?:\\d{2})"};
1317     case "F":
1318         return {g:1,
1319             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1320             s:"(" + Date.monthNames.join("|") + ")"};
1321     case "M":
1322         return {g:1,
1323             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1324             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1325     case "n":
1326         return {g:1,
1327             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1328             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1329     case "m":
1330         return {g:1,
1331             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1332             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1333     case "t":
1334         return {g:0,
1335             c:null,
1336             s:"\\d{1,2}"};
1337     case "L":
1338         return {g:0,
1339             c:null,
1340             s:"(?:1|0)"};
1341     case "Y":
1342         return {g:1,
1343             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1344             s:"(\\d{4})"};
1345     case "y":
1346         return {g:1,
1347             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1348                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1349             s:"(\\d{1,2})"};
1350     case "a":
1351         return {g:1,
1352             c:"if (results[" + currentGroup + "] == 'am') {\n"
1353                 + "if (h == 12) { h = 0; }\n"
1354                 + "} else { if (h < 12) { h += 12; }}",
1355             s:"(am|pm)"};
1356     case "A":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(AM|PM)"};
1362     case "g":
1363     case "G":
1364         return {g:1,
1365             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1366             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1367     case "h":
1368     case "H":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1372     case "i":
1373         return {g:1,
1374             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1375             s:"(\\d{2})"};
1376     case "s":
1377         return {g:1,
1378             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1379             s:"(\\d{2})"};
1380     case "O":
1381         return {g:1,
1382             c:[
1383                 "o = results[", currentGroup, "];\n",
1384                 "var sn = o.substring(0,1);\n", // get + / - sign
1385                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1386                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1387                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1388                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1389             ].join(""),
1390             s:"([+\-]\\d{4})"};
1391     case "P":
1392         return {g:1,
1393                 c:[
1394                    "o = results[", currentGroup, "];\n",
1395                    "var sn = o.substring(0,1);\n",
1396                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1397                    "var mn = o.substring(4,6) % 60;\n",
1398                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1399                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1400             ].join(""),
1401             s:"([+\-]\\d{4})"};
1402     case "T":
1403         return {g:0,
1404             c:null,
1405             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1406     case "Z":
1407         return {g:1,
1408             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1409                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1410             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1411     default:
1412         return {g:0,
1413             c:null,
1414             s:String.escape(character)};
1415     }
1416 };
1417
1418 /**
1419  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1420  * @return {String} The abbreviated timezone name (e.g. 'CST')
1421  */
1422 Date.prototype.getTimezone = function() {
1423     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1424 };
1425
1426 /**
1427  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1428  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1429  */
1430 Date.prototype.getGMTOffset = function() {
1431     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1432         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1433         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1434 };
1435
1436 /**
1437  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1438  * @return {String} 2-characters representing hours and 2-characters representing minutes
1439  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1440  */
1441 Date.prototype.getGMTColonOffset = function() {
1442         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1443                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1444                 + ":"
1445                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1446 }
1447
1448 /**
1449  * Get the numeric day number of the year, adjusted for leap year.
1450  * @return {Number} 0 through 364 (365 in leap years)
1451  */
1452 Date.prototype.getDayOfYear = function() {
1453     var num = 0;
1454     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1455     for (var i = 0; i < this.getMonth(); ++i) {
1456         num += Date.daysInMonth[i];
1457     }
1458     return num + this.getDate() - 1;
1459 };
1460
1461 /**
1462  * Get the string representation of the numeric week number of the year
1463  * (equivalent to the format specifier 'W').
1464  * @return {String} '00' through '52'
1465  */
1466 Date.prototype.getWeekOfYear = function() {
1467     // Skip to Thursday of this week
1468     var now = this.getDayOfYear() + (4 - this.getDay());
1469     // Find the first Thursday of the year
1470     var jan1 = new Date(this.getFullYear(), 0, 1);
1471     var then = (7 - jan1.getDay() + 4);
1472     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1473 };
1474
1475 /**
1476  * Whether or not the current date is in a leap year.
1477  * @return {Boolean} True if the current date is in a leap year, else false
1478  */
1479 Date.prototype.isLeapYear = function() {
1480     var year = this.getFullYear();
1481     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1482 };
1483
1484 /**
1485  * Get the first day of the current month, adjusted for leap year.  The returned value
1486  * is the numeric day index within the week (0-6) which can be used in conjunction with
1487  * the {@link #monthNames} array to retrieve the textual day name.
1488  * Example:
1489  *<pre><code>
1490 var dt = new Date('1/10/2007');
1491 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1492 </code></pre>
1493  * @return {Number} The day number (0-6)
1494  */
1495 Date.prototype.getFirstDayOfMonth = function() {
1496     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1497     return (day < 0) ? (day + 7) : day;
1498 };
1499
1500 /**
1501  * Get the last day of the current month, adjusted for leap year.  The returned value
1502  * is the numeric day index within the week (0-6) which can be used in conjunction with
1503  * the {@link #monthNames} array to retrieve the textual day name.
1504  * Example:
1505  *<pre><code>
1506 var dt = new Date('1/10/2007');
1507 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1508 </code></pre>
1509  * @return {Number} The day number (0-6)
1510  */
1511 Date.prototype.getLastDayOfMonth = function() {
1512     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1513     return (day < 0) ? (day + 7) : day;
1514 };
1515
1516
1517 /**
1518  * Get the first date of this date's month
1519  * @return {Date}
1520  */
1521 Date.prototype.getFirstDateOfMonth = function() {
1522     return new Date(this.getFullYear(), this.getMonth(), 1);
1523 };
1524
1525 /**
1526  * Get the last date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getLastDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1531 };
1532 /**
1533  * Get the number of days in the current month, adjusted for leap year.
1534  * @return {Number} The number of days in the month
1535  */
1536 Date.prototype.getDaysInMonth = function() {
1537     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1538     return Date.daysInMonth[this.getMonth()];
1539 };
1540
1541 /**
1542  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1543  * @return {String} 'st, 'nd', 'rd' or 'th'
1544  */
1545 Date.prototype.getSuffix = function() {
1546     switch (this.getDate()) {
1547         case 1:
1548         case 21:
1549         case 31:
1550             return "st";
1551         case 2:
1552         case 22:
1553             return "nd";
1554         case 3:
1555         case 23:
1556             return "rd";
1557         default:
1558             return "th";
1559     }
1560 };
1561
1562 // private
1563 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1564
1565 /**
1566  * An array of textual month names.
1567  * Override these values for international dates, for example...
1568  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1569  * @type Array
1570  * @static
1571  */
1572 Date.monthNames =
1573    ["January",
1574     "February",
1575     "March",
1576     "April",
1577     "May",
1578     "June",
1579     "July",
1580     "August",
1581     "September",
1582     "October",
1583     "November",
1584     "December"];
1585
1586 /**
1587  * An array of textual day names.
1588  * Override these values for international dates, for example...
1589  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1590  * @type Array
1591  * @static
1592  */
1593 Date.dayNames =
1594    ["Sunday",
1595     "Monday",
1596     "Tuesday",
1597     "Wednesday",
1598     "Thursday",
1599     "Friday",
1600     "Saturday"];
1601
1602 // private
1603 Date.y2kYear = 50;
1604 // private
1605 Date.monthNumbers = {
1606     Jan:0,
1607     Feb:1,
1608     Mar:2,
1609     Apr:3,
1610     May:4,
1611     Jun:5,
1612     Jul:6,
1613     Aug:7,
1614     Sep:8,
1615     Oct:9,
1616     Nov:10,
1617     Dec:11};
1618
1619 /**
1620  * Creates and returns a new Date instance with the exact same date value as the called instance.
1621  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1622  * variable will also be changed.  When the intention is to create a new variable that will not
1623  * modify the original instance, you should create a clone.
1624  *
1625  * Example of correctly cloning a date:
1626  * <pre><code>
1627 //wrong way:
1628 var orig = new Date('10/1/2006');
1629 var copy = orig;
1630 copy.setDate(5);
1631 document.write(orig);  //returns 'Thu Oct 05 2006'!
1632
1633 //correct way:
1634 var orig = new Date('10/1/2006');
1635 var copy = orig.clone();
1636 copy.setDate(5);
1637 document.write(orig);  //returns 'Thu Oct 01 2006'
1638 </code></pre>
1639  * @return {Date} The new Date instance
1640  */
1641 Date.prototype.clone = function() {
1642         return new Date(this.getTime());
1643 };
1644
1645 /**
1646  * Clears any time information from this date
1647  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1648  @return {Date} this or the clone
1649  */
1650 Date.prototype.clearTime = function(clone){
1651     if(clone){
1652         return this.clone().clearTime();
1653     }
1654     this.setHours(0);
1655     this.setMinutes(0);
1656     this.setSeconds(0);
1657     this.setMilliseconds(0);
1658     return this;
1659 };
1660
1661 // private
1662 // safari setMonth is broken
1663 if(Roo.isSafari){
1664     Date.brokenSetMonth = Date.prototype.setMonth;
1665         Date.prototype.setMonth = function(num){
1666                 if(num <= -1){
1667                         var n = Math.ceil(-num);
1668                         var back_year = Math.ceil(n/12);
1669                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1670                         this.setFullYear(this.getFullYear() - back_year);
1671                         return Date.brokenSetMonth.call(this, month);
1672                 } else {
1673                         return Date.brokenSetMonth.apply(this, arguments);
1674                 }
1675         };
1676 }
1677
1678 /** Date interval constant 
1679 * @static 
1680 * @type String */
1681 Date.MILLI = "ms";
1682 /** Date interval constant 
1683 * @static 
1684 * @type String */
1685 Date.SECOND = "s";
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MINUTE = "mi";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.HOUR = "h";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.DAY = "d";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.MONTH = "mo";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.YEAR = "y";
1706
1707 /**
1708  * Provides a convenient method of performing basic date arithmetic.  This method
1709  * does not modify the Date instance being called - it creates and returns
1710  * a new Date instance containing the resulting date value.
1711  *
1712  * Examples:
1713  * <pre><code>
1714 //Basic usage:
1715 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1716 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1717
1718 //Negative values will subtract correctly:
1719 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1720 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1721
1722 //You can even chain several calls together in one line!
1723 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1724 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1725  </code></pre>
1726  *
1727  * @param {String} interval   A valid date interval enum value
1728  * @param {Number} value      The amount to add to the current date
1729  * @return {Date} The new Date instance
1730  */
1731 Date.prototype.add = function(interval, value){
1732   var d = this.clone();
1733   if (!interval || value === 0) return d;
1734   switch(interval.toLowerCase()){
1735     case Date.MILLI:
1736       d.setMilliseconds(this.getMilliseconds() + value);
1737       break;
1738     case Date.SECOND:
1739       d.setSeconds(this.getSeconds() + value);
1740       break;
1741     case Date.MINUTE:
1742       d.setMinutes(this.getMinutes() + value);
1743       break;
1744     case Date.HOUR:
1745       d.setHours(this.getHours() + value);
1746       break;
1747     case Date.DAY:
1748       d.setDate(this.getDate() + value);
1749       break;
1750     case Date.MONTH:
1751       var day = this.getDate();
1752       if(day > 28){
1753           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1754       }
1755       d.setDate(day);
1756       d.setMonth(this.getMonth() + value);
1757       break;
1758     case Date.YEAR:
1759       d.setFullYear(this.getFullYear() + value);
1760       break;
1761   }
1762   return d;
1763 };
1764 /*
1765  * Based on:
1766  * Ext JS Library 1.1.1
1767  * Copyright(c) 2006-2007, Ext JS, LLC.
1768  *
1769  * Originally Released Under LGPL - original licence link has changed is not relivant.
1770  *
1771  * Fork - LGPL
1772  * <script type="text/javascript">
1773  */
1774
1775 Roo.lib.Dom = {
1776     getViewWidth : function(full) {
1777         return full ? this.getDocumentWidth() : this.getViewportWidth();
1778     },
1779
1780     getViewHeight : function(full) {
1781         return full ? this.getDocumentHeight() : this.getViewportHeight();
1782     },
1783
1784     getDocumentHeight: function() {
1785         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1786         return Math.max(scrollHeight, this.getViewportHeight());
1787     },
1788
1789     getDocumentWidth: function() {
1790         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1791         return Math.max(scrollWidth, this.getViewportWidth());
1792     },
1793
1794     getViewportHeight: function() {
1795         var height = self.innerHeight;
1796         var mode = document.compatMode;
1797
1798         if ((mode || Roo.isIE) && !Roo.isOpera) {
1799             height = (mode == "CSS1Compat") ?
1800                      document.documentElement.clientHeight :
1801                      document.body.clientHeight;
1802         }
1803
1804         return height;
1805     },
1806
1807     getViewportWidth: function() {
1808         var width = self.innerWidth;
1809         var mode = document.compatMode;
1810
1811         if (mode || Roo.isIE) {
1812             width = (mode == "CSS1Compat") ?
1813                     document.documentElement.clientWidth :
1814                     document.body.clientWidth;
1815         }
1816         return width;
1817     },
1818
1819     isAncestor : function(p, c) {
1820         p = Roo.getDom(p);
1821         c = Roo.getDom(c);
1822         if (!p || !c) {
1823             return false;
1824         }
1825
1826         if (p.contains && !Roo.isSafari) {
1827             return p.contains(c);
1828         } else if (p.compareDocumentPosition) {
1829             return !!(p.compareDocumentPosition(c) & 16);
1830         } else {
1831             var parent = c.parentNode;
1832             while (parent) {
1833                 if (parent == p) {
1834                     return true;
1835                 }
1836                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1837                     return false;
1838                 }
1839                 parent = parent.parentNode;
1840             }
1841             return false;
1842         }
1843     },
1844
1845     getRegion : function(el) {
1846         return Roo.lib.Region.getRegion(el);
1847     },
1848
1849     getY : function(el) {
1850         return this.getXY(el)[1];
1851     },
1852
1853     getX : function(el) {
1854         return this.getXY(el)[0];
1855     },
1856
1857     getXY : function(el) {
1858         var p, pe, b, scroll, bd = document.body;
1859         el = Roo.getDom(el);
1860         var fly = Roo.lib.AnimBase.fly;
1861         if (el.getBoundingClientRect) {
1862             b = el.getBoundingClientRect();
1863             scroll = fly(document).getScroll();
1864             return [b.left + scroll.left, b.top + scroll.top];
1865         }
1866         var x = 0, y = 0;
1867
1868         p = el;
1869
1870         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1871
1872         while (p) {
1873
1874             x += p.offsetLeft;
1875             y += p.offsetTop;
1876
1877             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1878                 hasAbsolute = true;
1879             }
1880
1881             if (Roo.isGecko) {
1882                 pe = fly(p);
1883
1884                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1885                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1886
1887
1888                 x += bl;
1889                 y += bt;
1890
1891
1892                 if (p != el && pe.getStyle('overflow') != 'visible') {
1893                     x += bl;
1894                     y += bt;
1895                 }
1896             }
1897             p = p.offsetParent;
1898         }
1899
1900         if (Roo.isSafari && hasAbsolute) {
1901             x -= bd.offsetLeft;
1902             y -= bd.offsetTop;
1903         }
1904
1905         if (Roo.isGecko && !hasAbsolute) {
1906             var dbd = fly(bd);
1907             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1908             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1909         }
1910
1911         p = el.parentNode;
1912         while (p && p != bd) {
1913             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1914                 x -= p.scrollLeft;
1915                 y -= p.scrollTop;
1916             }
1917             p = p.parentNode;
1918         }
1919         return [x, y];
1920     },
1921  
1922   
1923
1924
1925     setXY : function(el, xy) {
1926         el = Roo.fly(el, '_setXY');
1927         el.position();
1928         var pts = el.translatePoints(xy);
1929         if (xy[0] !== false) {
1930             el.dom.style.left = pts.left + "px";
1931         }
1932         if (xy[1] !== false) {
1933             el.dom.style.top = pts.top + "px";
1934         }
1935     },
1936
1937     setX : function(el, x) {
1938         this.setXY(el, [x, false]);
1939     },
1940
1941     setY : function(el, y) {
1942         this.setXY(el, [false, y]);
1943     }
1944 };
1945 /*
1946  * Portions of this file are based on pieces of Yahoo User Interface Library
1947  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1948  * YUI licensed under the BSD License:
1949  * http://developer.yahoo.net/yui/license.txt
1950  * <script type="text/javascript">
1951  *
1952  */
1953
1954 Roo.lib.Event = function() {
1955     var loadComplete = false;
1956     var listeners = [];
1957     var unloadListeners = [];
1958     var retryCount = 0;
1959     var onAvailStack = [];
1960     var counter = 0;
1961     var lastError = null;
1962
1963     return {
1964         POLL_RETRYS: 200,
1965         POLL_INTERVAL: 20,
1966         EL: 0,
1967         TYPE: 1,
1968         FN: 2,
1969         WFN: 3,
1970         OBJ: 3,
1971         ADJ_SCOPE: 4,
1972         _interval: null,
1973
1974         startInterval: function() {
1975             if (!this._interval) {
1976                 var self = this;
1977                 var callback = function() {
1978                     self._tryPreloadAttach();
1979                 };
1980                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1981
1982             }
1983         },
1984
1985         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1986             onAvailStack.push({ id:         p_id,
1987                 fn:         p_fn,
1988                 obj:        p_obj,
1989                 override:   p_override,
1990                 checkReady: false    });
1991
1992             retryCount = this.POLL_RETRYS;
1993             this.startInterval();
1994         },
1995
1996
1997         addListener: function(el, eventName, fn) {
1998             el = Roo.getDom(el);
1999             if (!el || !fn) {
2000                 return false;
2001             }
2002
2003             if ("unload" == eventName) {
2004                 unloadListeners[unloadListeners.length] =
2005                 [el, eventName, fn];
2006                 return true;
2007             }
2008
2009             var wrappedFn = function(e) {
2010                 return fn(Roo.lib.Event.getEvent(e));
2011             };
2012
2013             var li = [el, eventName, fn, wrappedFn];
2014
2015             var index = listeners.length;
2016             listeners[index] = li;
2017
2018             this.doAdd(el, eventName, wrappedFn, false);
2019             return true;
2020
2021         },
2022
2023
2024         removeListener: function(el, eventName, fn) {
2025             var i, len;
2026
2027             el = Roo.getDom(el);
2028
2029             if(!fn) {
2030                 return this.purgeElement(el, false, eventName);
2031             }
2032
2033
2034             if ("unload" == eventName) {
2035
2036                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2037                     var li = unloadListeners[i];
2038                     if (li &&
2039                         li[0] == el &&
2040                         li[1] == eventName &&
2041                         li[2] == fn) {
2042                         unloadListeners.splice(i, 1);
2043                         return true;
2044                     }
2045                 }
2046
2047                 return false;
2048             }
2049
2050             var cacheItem = null;
2051
2052
2053             var index = arguments[3];
2054
2055             if ("undefined" == typeof index) {
2056                 index = this._getCacheIndex(el, eventName, fn);
2057             }
2058
2059             if (index >= 0) {
2060                 cacheItem = listeners[index];
2061             }
2062
2063             if (!el || !cacheItem) {
2064                 return false;
2065             }
2066
2067             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2068
2069             delete listeners[index][this.WFN];
2070             delete listeners[index][this.FN];
2071             listeners.splice(index, 1);
2072
2073             return true;
2074
2075         },
2076
2077
2078         getTarget: function(ev, resolveTextNode) {
2079             ev = ev.browserEvent || ev;
2080             var t = ev.target || ev.srcElement;
2081             return this.resolveTextNode(t);
2082         },
2083
2084
2085         resolveTextNode: function(node) {
2086             if (Roo.isSafari && node && 3 == node.nodeType) {
2087                 return node.parentNode;
2088             } else {
2089                 return node;
2090             }
2091         },
2092
2093
2094         getPageX: function(ev) {
2095             ev = ev.browserEvent || ev;
2096             var x = ev.pageX;
2097             if (!x && 0 !== x) {
2098                 x = ev.clientX || 0;
2099
2100                 if (Roo.isIE) {
2101                     x += this.getScroll()[1];
2102                 }
2103             }
2104
2105             return x;
2106         },
2107
2108
2109         getPageY: function(ev) {
2110             ev = ev.browserEvent || ev;
2111             var y = ev.pageY;
2112             if (!y && 0 !== y) {
2113                 y = ev.clientY || 0;
2114
2115                 if (Roo.isIE) {
2116                     y += this.getScroll()[0];
2117                 }
2118             }
2119
2120
2121             return y;
2122         },
2123
2124
2125         getXY: function(ev) {
2126             ev = ev.browserEvent || ev;
2127             return [this.getPageX(ev), this.getPageY(ev)];
2128         },
2129
2130
2131         getRelatedTarget: function(ev) {
2132             ev = ev.browserEvent || ev;
2133             var t = ev.relatedTarget;
2134             if (!t) {
2135                 if (ev.type == "mouseout") {
2136                     t = ev.toElement;
2137                 } else if (ev.type == "mouseover") {
2138                     t = ev.fromElement;
2139                 }
2140             }
2141
2142             return this.resolveTextNode(t);
2143         },
2144
2145
2146         getTime: function(ev) {
2147             ev = ev.browserEvent || ev;
2148             if (!ev.time) {
2149                 var t = new Date().getTime();
2150                 try {
2151                     ev.time = t;
2152                 } catch(ex) {
2153                     this.lastError = ex;
2154                     return t;
2155                 }
2156             }
2157
2158             return ev.time;
2159         },
2160
2161
2162         stopEvent: function(ev) {
2163             this.stopPropagation(ev);
2164             this.preventDefault(ev);
2165         },
2166
2167
2168         stopPropagation: function(ev) {
2169             ev = ev.browserEvent || ev;
2170             if (ev.stopPropagation) {
2171                 ev.stopPropagation();
2172             } else {
2173                 ev.cancelBubble = true;
2174             }
2175         },
2176
2177
2178         preventDefault: function(ev) {
2179             ev = ev.browserEvent || ev;
2180             if(ev.preventDefault) {
2181                 ev.preventDefault();
2182             } else {
2183                 ev.returnValue = false;
2184             }
2185         },
2186
2187
2188         getEvent: function(e) {
2189             var ev = e || window.event;
2190             if (!ev) {
2191                 var c = this.getEvent.caller;
2192                 while (c) {
2193                     ev = c.arguments[0];
2194                     if (ev && Event == ev.constructor) {
2195                         break;
2196                     }
2197                     c = c.caller;
2198                 }
2199             }
2200             return ev;
2201         },
2202
2203
2204         getCharCode: function(ev) {
2205             ev = ev.browserEvent || ev;
2206             return ev.charCode || ev.keyCode || 0;
2207         },
2208
2209
2210         _getCacheIndex: function(el, eventName, fn) {
2211             for (var i = 0,len = listeners.length; i < len; ++i) {
2212                 var li = listeners[i];
2213                 if (li &&
2214                     li[this.FN] == fn &&
2215                     li[this.EL] == el &&
2216                     li[this.TYPE] == eventName) {
2217                     return i;
2218                 }
2219             }
2220
2221             return -1;
2222         },
2223
2224
2225         elCache: {},
2226
2227
2228         getEl: function(id) {
2229             return document.getElementById(id);
2230         },
2231
2232
2233         clearCache: function() {
2234         },
2235
2236
2237         _load: function(e) {
2238             loadComplete = true;
2239             var EU = Roo.lib.Event;
2240
2241
2242             if (Roo.isIE) {
2243                 EU.doRemove(window, "load", EU._load);
2244             }
2245         },
2246
2247
2248         _tryPreloadAttach: function() {
2249
2250             if (this.locked) {
2251                 return false;
2252             }
2253
2254             this.locked = true;
2255
2256
2257             var tryAgain = !loadComplete;
2258             if (!tryAgain) {
2259                 tryAgain = (retryCount > 0);
2260             }
2261
2262
2263             var notAvail = [];
2264             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2265                 var item = onAvailStack[i];
2266                 if (item) {
2267                     var el = this.getEl(item.id);
2268
2269                     if (el) {
2270                         if (!item.checkReady ||
2271                             loadComplete ||
2272                             el.nextSibling ||
2273                             (document && document.body)) {
2274
2275                             var scope = el;
2276                             if (item.override) {
2277                                 if (item.override === true) {
2278                                     scope = item.obj;
2279                                 } else {
2280                                     scope = item.override;
2281                                 }
2282                             }
2283                             item.fn.call(scope, item.obj);
2284                             onAvailStack[i] = null;
2285                         }
2286                     } else {
2287                         notAvail.push(item);
2288                     }
2289                 }
2290             }
2291
2292             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2293
2294             if (tryAgain) {
2295
2296                 this.startInterval();
2297             } else {
2298                 clearInterval(this._interval);
2299                 this._interval = null;
2300             }
2301
2302             this.locked = false;
2303
2304             return true;
2305
2306         },
2307
2308
2309         purgeElement: function(el, recurse, eventName) {
2310             var elListeners = this.getListeners(el, eventName);
2311             if (elListeners) {
2312                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2313                     var l = elListeners[i];
2314                     this.removeListener(el, l.type, l.fn);
2315                 }
2316             }
2317
2318             if (recurse && el && el.childNodes) {
2319                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2320                     this.purgeElement(el.childNodes[i], recurse, eventName);
2321                 }
2322             }
2323         },
2324
2325
2326         getListeners: function(el, eventName) {
2327             var results = [], searchLists;
2328             if (!eventName) {
2329                 searchLists = [listeners, unloadListeners];
2330             } else if (eventName == "unload") {
2331                 searchLists = [unloadListeners];
2332             } else {
2333                 searchLists = [listeners];
2334             }
2335
2336             for (var j = 0; j < searchLists.length; ++j) {
2337                 var searchList = searchLists[j];
2338                 if (searchList && searchList.length > 0) {
2339                     for (var i = 0,len = searchList.length; i < len; ++i) {
2340                         var l = searchList[i];
2341                         if (l && l[this.EL] === el &&
2342                             (!eventName || eventName === l[this.TYPE])) {
2343                             results.push({
2344                                 type:   l[this.TYPE],
2345                                 fn:     l[this.FN],
2346                                 obj:    l[this.OBJ],
2347                                 adjust: l[this.ADJ_SCOPE],
2348                                 index:  i
2349                             });
2350                         }
2351                     }
2352                 }
2353             }
2354
2355             return (results.length) ? results : null;
2356         },
2357
2358
2359         _unload: function(e) {
2360
2361             var EU = Roo.lib.Event, i, j, l, len, index;
2362
2363             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2364                 l = unloadListeners[i];
2365                 if (l) {
2366                     var scope = window;
2367                     if (l[EU.ADJ_SCOPE]) {
2368                         if (l[EU.ADJ_SCOPE] === true) {
2369                             scope = l[EU.OBJ];
2370                         } else {
2371                             scope = l[EU.ADJ_SCOPE];
2372                         }
2373                     }
2374                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2375                     unloadListeners[i] = null;
2376                     l = null;
2377                     scope = null;
2378                 }
2379             }
2380
2381             unloadListeners = null;
2382
2383             if (listeners && listeners.length > 0) {
2384                 j = listeners.length;
2385                 while (j) {
2386                     index = j - 1;
2387                     l = listeners[index];
2388                     if (l) {
2389                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2390                                 l[EU.FN], index);
2391                     }
2392                     j = j - 1;
2393                 }
2394                 l = null;
2395
2396                 EU.clearCache();
2397             }
2398
2399             EU.doRemove(window, "unload", EU._unload);
2400
2401         },
2402
2403
2404         getScroll: function() {
2405             var dd = document.documentElement, db = document.body;
2406             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2407                 return [dd.scrollTop, dd.scrollLeft];
2408             } else if (db) {
2409                 return [db.scrollTop, db.scrollLeft];
2410             } else {
2411                 return [0, 0];
2412             }
2413         },
2414
2415
2416         doAdd: function () {
2417             if (window.addEventListener) {
2418                 return function(el, eventName, fn, capture) {
2419                     el.addEventListener(eventName, fn, (capture));
2420                 };
2421             } else if (window.attachEvent) {
2422                 return function(el, eventName, fn, capture) {
2423                     el.attachEvent("on" + eventName, fn);
2424                 };
2425             } else {
2426                 return function() {
2427                 };
2428             }
2429         }(),
2430
2431
2432         doRemove: function() {
2433             if (window.removeEventListener) {
2434                 return function (el, eventName, fn, capture) {
2435                     el.removeEventListener(eventName, fn, (capture));
2436                 };
2437             } else if (window.detachEvent) {
2438                 return function (el, eventName, fn) {
2439                     el.detachEvent("on" + eventName, fn);
2440                 };
2441             } else {
2442                 return function() {
2443                 };
2444             }
2445         }()
2446     };
2447     
2448 }();
2449 (function() {     
2450    
2451     var E = Roo.lib.Event;
2452     E.on = E.addListener;
2453     E.un = E.removeListener;
2454
2455     if (document && document.body) {
2456         E._load();
2457     } else {
2458         E.doAdd(window, "load", E._load);
2459     }
2460     E.doAdd(window, "unload", E._unload);
2461     E._tryPreloadAttach();
2462 })();
2463
2464 /*
2465  * Portions of this file are based on pieces of Yahoo User Interface Library
2466  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2467  * YUI licensed under the BSD License:
2468  * http://developer.yahoo.net/yui/license.txt
2469  * <script type="text/javascript">
2470  *
2471  */
2472
2473 (function() {
2474     /**
2475      * @class Roo.lib.Ajax
2476      *
2477      */
2478     Roo.lib.Ajax = {
2479         /**
2480          * @static 
2481          */
2482         request : function(method, uri, cb, data, options) {
2483             if(options){
2484                 var hs = options.headers;
2485                 if(hs){
2486                     for(var h in hs){
2487                         if(hs.hasOwnProperty(h)){
2488                             this.initHeader(h, hs[h], false);
2489                         }
2490                     }
2491                 }
2492                 if(options.xmlData){
2493                     this.initHeader('Content-Type', 'text/xml', false);
2494                     method = 'POST';
2495                     data = options.xmlData;
2496                 }
2497             }
2498
2499             return this.asyncRequest(method, uri, cb, data);
2500         },
2501
2502         serializeForm : function(form) {
2503             if(typeof form == 'string') {
2504                 form = (document.getElementById(form) || document.forms[form]);
2505             }
2506
2507             var el, name, val, disabled, data = '', hasSubmit = false;
2508             for (var i = 0; i < form.elements.length; i++) {
2509                 el = form.elements[i];
2510                 disabled = form.elements[i].disabled;
2511                 name = form.elements[i].name;
2512                 val = form.elements[i].value;
2513
2514                 if (!disabled && name){
2515                     switch (el.type)
2516                             {
2517                         case 'select-one':
2518                         case 'select-multiple':
2519                             for (var j = 0; j < el.options.length; j++) {
2520                                 if (el.options[j].selected) {
2521                                     if (Roo.isIE) {
2522                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2523                                     }
2524                                     else {
2525                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2526                                     }
2527                                 }
2528                             }
2529                             break;
2530                         case 'radio':
2531                         case 'checkbox':
2532                             if (el.checked) {
2533                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2534                             }
2535                             break;
2536                         case 'file':
2537
2538                         case undefined:
2539
2540                         case 'reset':
2541
2542                         case 'button':
2543
2544                             break;
2545                         case 'submit':
2546                             if(hasSubmit == false) {
2547                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2548                                 hasSubmit = true;
2549                             }
2550                             break;
2551                         default:
2552                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2553                             break;
2554                     }
2555                 }
2556             }
2557             data = data.substr(0, data.length - 1);
2558             return data;
2559         },
2560
2561         headers:{},
2562
2563         hasHeaders:false,
2564
2565         useDefaultHeader:true,
2566
2567         defaultPostHeader:'application/x-www-form-urlencoded',
2568
2569         useDefaultXhrHeader:true,
2570
2571         defaultXhrHeader:'XMLHttpRequest',
2572
2573         hasDefaultHeaders:true,
2574
2575         defaultHeaders:{},
2576
2577         poll:{},
2578
2579         timeout:{},
2580
2581         pollInterval:50,
2582
2583         transactionId:0,
2584
2585         setProgId:function(id)
2586         {
2587             this.activeX.unshift(id);
2588         },
2589
2590         setDefaultPostHeader:function(b)
2591         {
2592             this.useDefaultHeader = b;
2593         },
2594
2595         setDefaultXhrHeader:function(b)
2596         {
2597             this.useDefaultXhrHeader = b;
2598         },
2599
2600         setPollingInterval:function(i)
2601         {
2602             if (typeof i == 'number' && isFinite(i)) {
2603                 this.pollInterval = i;
2604             }
2605         },
2606
2607         createXhrObject:function(transactionId)
2608         {
2609             var obj,http;
2610             try
2611             {
2612
2613                 http = new XMLHttpRequest();
2614
2615                 obj = { conn:http, tId:transactionId };
2616             }
2617             catch(e)
2618             {
2619                 for (var i = 0; i < this.activeX.length; ++i) {
2620                     try
2621                     {
2622
2623                         http = new ActiveXObject(this.activeX[i]);
2624
2625                         obj = { conn:http, tId:transactionId };
2626                         break;
2627                     }
2628                     catch(e) {
2629                     }
2630                 }
2631             }
2632             finally
2633             {
2634                 return obj;
2635             }
2636         },
2637
2638         getConnectionObject:function()
2639         {
2640             var o;
2641             var tId = this.transactionId;
2642
2643             try
2644             {
2645                 o = this.createXhrObject(tId);
2646                 if (o) {
2647                     this.transactionId++;
2648                 }
2649             }
2650             catch(e) {
2651             }
2652             finally
2653             {
2654                 return o;
2655             }
2656         },
2657
2658         asyncRequest:function(method, uri, callback, postData)
2659         {
2660             var o = this.getConnectionObject();
2661
2662             if (!o) {
2663                 return null;
2664             }
2665             else {
2666                 o.conn.open(method, uri, true);
2667
2668                 if (this.useDefaultXhrHeader) {
2669                     if (!this.defaultHeaders['X-Requested-With']) {
2670                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2671                     }
2672                 }
2673
2674                 if(postData && this.useDefaultHeader){
2675                     this.initHeader('Content-Type', this.defaultPostHeader);
2676                 }
2677
2678                  if (this.hasDefaultHeaders || this.hasHeaders) {
2679                     this.setHeader(o);
2680                 }
2681
2682                 this.handleReadyState(o, callback);
2683                 o.conn.send(postData || null);
2684
2685                 return o;
2686             }
2687         },
2688
2689         handleReadyState:function(o, callback)
2690         {
2691             var oConn = this;
2692
2693             if (callback && callback.timeout) {
2694                 this.timeout[o.tId] = window.setTimeout(function() {
2695                     oConn.abort(o, callback, true);
2696                 }, callback.timeout);
2697             }
2698
2699             this.poll[o.tId] = window.setInterval(
2700                     function() {
2701                         if (o.conn && o.conn.readyState == 4) {
2702                             window.clearInterval(oConn.poll[o.tId]);
2703                             delete oConn.poll[o.tId];
2704
2705                             if(callback && callback.timeout) {
2706                                 window.clearTimeout(oConn.timeout[o.tId]);
2707                                 delete oConn.timeout[o.tId];
2708                             }
2709
2710                             oConn.handleTransactionResponse(o, callback);
2711                         }
2712                     }
2713                     , this.pollInterval);
2714         },
2715
2716         handleTransactionResponse:function(o, callback, isAbort)
2717         {
2718
2719             if (!callback) {
2720                 this.releaseObject(o);
2721                 return;
2722             }
2723
2724             var httpStatus, responseObject;
2725
2726             try
2727             {
2728                 if (o.conn.status !== undefined && o.conn.status != 0) {
2729                     httpStatus = o.conn.status;
2730                 }
2731                 else {
2732                     httpStatus = 13030;
2733                 }
2734             }
2735             catch(e) {
2736
2737
2738                 httpStatus = 13030;
2739             }
2740
2741             if (httpStatus >= 200 && httpStatus < 300) {
2742                 responseObject = this.createResponseObject(o, callback.argument);
2743                 if (callback.success) {
2744                     if (!callback.scope) {
2745                         callback.success(responseObject);
2746                     }
2747                     else {
2748
2749
2750                         callback.success.apply(callback.scope, [responseObject]);
2751                     }
2752                 }
2753             }
2754             else {
2755                 switch (httpStatus) {
2756
2757                     case 12002:
2758                     case 12029:
2759                     case 12030:
2760                     case 12031:
2761                     case 12152:
2762                     case 13030:
2763                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2764                         if (callback.failure) {
2765                             if (!callback.scope) {
2766                                 callback.failure(responseObject);
2767                             }
2768                             else {
2769                                 callback.failure.apply(callback.scope, [responseObject]);
2770                             }
2771                         }
2772                         break;
2773                     default:
2774                         responseObject = this.createResponseObject(o, callback.argument);
2775                         if (callback.failure) {
2776                             if (!callback.scope) {
2777                                 callback.failure(responseObject);
2778                             }
2779                             else {
2780                                 callback.failure.apply(callback.scope, [responseObject]);
2781                             }
2782                         }
2783                 }
2784             }
2785
2786             this.releaseObject(o);
2787             responseObject = null;
2788         },
2789
2790         createResponseObject:function(o, callbackArg)
2791         {
2792             var obj = {};
2793             var headerObj = {};
2794
2795             try
2796             {
2797                 var headerStr = o.conn.getAllResponseHeaders();
2798                 var header = headerStr.split('\n');
2799                 for (var i = 0; i < header.length; i++) {
2800                     var delimitPos = header[i].indexOf(':');
2801                     if (delimitPos != -1) {
2802                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2803                     }
2804                 }
2805             }
2806             catch(e) {
2807             }
2808
2809             obj.tId = o.tId;
2810             obj.status = o.conn.status;
2811             obj.statusText = o.conn.statusText;
2812             obj.getResponseHeader = headerObj;
2813             obj.getAllResponseHeaders = headerStr;
2814             obj.responseText = o.conn.responseText;
2815             obj.responseXML = o.conn.responseXML;
2816
2817             if (typeof callbackArg !== undefined) {
2818                 obj.argument = callbackArg;
2819             }
2820
2821             return obj;
2822         },
2823
2824         createExceptionObject:function(tId, callbackArg, isAbort)
2825         {
2826             var COMM_CODE = 0;
2827             var COMM_ERROR = 'communication failure';
2828             var ABORT_CODE = -1;
2829             var ABORT_ERROR = 'transaction aborted';
2830
2831             var obj = {};
2832
2833             obj.tId = tId;
2834             if (isAbort) {
2835                 obj.status = ABORT_CODE;
2836                 obj.statusText = ABORT_ERROR;
2837             }
2838             else {
2839                 obj.status = COMM_CODE;
2840                 obj.statusText = COMM_ERROR;
2841             }
2842
2843             if (callbackArg) {
2844                 obj.argument = callbackArg;
2845             }
2846
2847             return obj;
2848         },
2849
2850         initHeader:function(label, value, isDefault)
2851         {
2852             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2853
2854             if (headerObj[label] === undefined) {
2855                 headerObj[label] = value;
2856             }
2857             else {
2858
2859
2860                 headerObj[label] = value + "," + headerObj[label];
2861             }
2862
2863             if (isDefault) {
2864                 this.hasDefaultHeaders = true;
2865             }
2866             else {
2867                 this.hasHeaders = true;
2868             }
2869         },
2870
2871
2872         setHeader:function(o)
2873         {
2874             if (this.hasDefaultHeaders) {
2875                 for (var prop in this.defaultHeaders) {
2876                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2877                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2878                     }
2879                 }
2880             }
2881
2882             if (this.hasHeaders) {
2883                 for (var prop in this.headers) {
2884                     if (this.headers.hasOwnProperty(prop)) {
2885                         o.conn.setRequestHeader(prop, this.headers[prop]);
2886                     }
2887                 }
2888                 this.headers = {};
2889                 this.hasHeaders = false;
2890             }
2891         },
2892
2893         resetDefaultHeaders:function() {
2894             delete this.defaultHeaders;
2895             this.defaultHeaders = {};
2896             this.hasDefaultHeaders = false;
2897         },
2898
2899         abort:function(o, callback, isTimeout)
2900         {
2901             if(this.isCallInProgress(o)) {
2902                 o.conn.abort();
2903                 window.clearInterval(this.poll[o.tId]);
2904                 delete this.poll[o.tId];
2905                 if (isTimeout) {
2906                     delete this.timeout[o.tId];
2907                 }
2908
2909                 this.handleTransactionResponse(o, callback, true);
2910
2911                 return true;
2912             }
2913             else {
2914                 return false;
2915             }
2916         },
2917
2918
2919         isCallInProgress:function(o)
2920         {
2921             if (o && o.conn) {
2922                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2923             }
2924             else {
2925
2926                 return false;
2927             }
2928         },
2929
2930
2931         releaseObject:function(o)
2932         {
2933
2934             o.conn = null;
2935
2936             o = null;
2937         },
2938
2939         activeX:[
2940         'MSXML2.XMLHTTP.3.0',
2941         'MSXML2.XMLHTTP',
2942         'Microsoft.XMLHTTP'
2943         ]
2944
2945
2946     };
2947 })();/*
2948  * Portions of this file are based on pieces of Yahoo User Interface Library
2949  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2950  * YUI licensed under the BSD License:
2951  * http://developer.yahoo.net/yui/license.txt
2952  * <script type="text/javascript">
2953  *
2954  */
2955
2956 Roo.lib.Region = function(t, r, b, l) {
2957     this.top = t;
2958     this[1] = t;
2959     this.right = r;
2960     this.bottom = b;
2961     this.left = l;
2962     this[0] = l;
2963 };
2964
2965
2966 Roo.lib.Region.prototype = {
2967     contains : function(region) {
2968         return ( region.left >= this.left &&
2969                  region.right <= this.right &&
2970                  region.top >= this.top &&
2971                  region.bottom <= this.bottom    );
2972
2973     },
2974
2975     getArea : function() {
2976         return ( (this.bottom - this.top) * (this.right - this.left) );
2977     },
2978
2979     intersect : function(region) {
2980         var t = Math.max(this.top, region.top);
2981         var r = Math.min(this.right, region.right);
2982         var b = Math.min(this.bottom, region.bottom);
2983         var l = Math.max(this.left, region.left);
2984
2985         if (b >= t && r >= l) {
2986             return new Roo.lib.Region(t, r, b, l);
2987         } else {
2988             return null;
2989         }
2990     },
2991     union : function(region) {
2992         var t = Math.min(this.top, region.top);
2993         var r = Math.max(this.right, region.right);
2994         var b = Math.max(this.bottom, region.bottom);
2995         var l = Math.min(this.left, region.left);
2996
2997         return new Roo.lib.Region(t, r, b, l);
2998     },
2999
3000     adjust : function(t, l, b, r) {
3001         this.top += t;
3002         this.left += l;
3003         this.right += r;
3004         this.bottom += b;
3005         return this;
3006     }
3007 };
3008
3009 Roo.lib.Region.getRegion = function(el) {
3010     var p = Roo.lib.Dom.getXY(el);
3011
3012     var t = p[1];
3013     var r = p[0] + el.offsetWidth;
3014     var b = p[1] + el.offsetHeight;
3015     var l = p[0];
3016
3017     return new Roo.lib.Region(t, r, b, l);
3018 };
3019 /*
3020  * Portions of this file are based on pieces of Yahoo User Interface Library
3021  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3022  * YUI licensed under the BSD License:
3023  * http://developer.yahoo.net/yui/license.txt
3024  * <script type="text/javascript">
3025  *
3026  */
3027 //@@dep Roo.lib.Region
3028
3029
3030 Roo.lib.Point = function(x, y) {
3031     if (x instanceof Array) {
3032         y = x[1];
3033         x = x[0];
3034     }
3035     this.x = this.right = this.left = this[0] = x;
3036     this.y = this.top = this.bottom = this[1] = y;
3037 };
3038
3039 Roo.lib.Point.prototype = new Roo.lib.Region();
3040 /*
3041  * Portions of this file are based on pieces of Yahoo User Interface Library
3042  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3043  * YUI licensed under the BSD License:
3044  * http://developer.yahoo.net/yui/license.txt
3045  * <script type="text/javascript">
3046  *
3047  */
3048  
3049 (function() {   
3050
3051     Roo.lib.Anim = {
3052         scroll : function(el, args, duration, easing, cb, scope) {
3053             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3054         },
3055
3056         motion : function(el, args, duration, easing, cb, scope) {
3057             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3058         },
3059
3060         color : function(el, args, duration, easing, cb, scope) {
3061             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3062         },
3063
3064         run : function(el, args, duration, easing, cb, scope, type) {
3065             type = type || Roo.lib.AnimBase;
3066             if (typeof easing == "string") {
3067                 easing = Roo.lib.Easing[easing];
3068             }
3069             var anim = new type(el, args, duration, easing);
3070             anim.animateX(function() {
3071                 Roo.callback(cb, scope);
3072             });
3073             return anim;
3074         }
3075     };
3076 })();/*
3077  * Portions of this file are based on pieces of Yahoo User Interface Library
3078  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3079  * YUI licensed under the BSD License:
3080  * http://developer.yahoo.net/yui/license.txt
3081  * <script type="text/javascript">
3082  *
3083  */
3084
3085 (function() {    
3086     var libFlyweight;
3087     
3088     function fly(el) {
3089         if (!libFlyweight) {
3090             libFlyweight = new Roo.Element.Flyweight();
3091         }
3092         libFlyweight.dom = el;
3093         return libFlyweight;
3094     }
3095
3096     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3097     
3098    
3099     
3100     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3101         if (el) {
3102             this.init(el, attributes, duration, method);
3103         }
3104     };
3105
3106     Roo.lib.AnimBase.fly = fly;
3107     
3108     
3109     
3110     Roo.lib.AnimBase.prototype = {
3111
3112         toString: function() {
3113             var el = this.getEl();
3114             var id = el.id || el.tagName;
3115             return ("Anim " + id);
3116         },
3117
3118         patterns: {
3119             noNegatives:        /width|height|opacity|padding/i,
3120             offsetAttribute:  /^((width|height)|(top|left))$/,
3121             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3122             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3123         },
3124
3125
3126         doMethod: function(attr, start, end) {
3127             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3128         },
3129
3130
3131         setAttribute: function(attr, val, unit) {
3132             if (this.patterns.noNegatives.test(attr)) {
3133                 val = (val > 0) ? val : 0;
3134             }
3135
3136             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3137         },
3138
3139
3140         getAttribute: function(attr) {
3141             var el = this.getEl();
3142             var val = fly(el).getStyle(attr);
3143
3144             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3145                 return parseFloat(val);
3146             }
3147
3148             var a = this.patterns.offsetAttribute.exec(attr) || [];
3149             var pos = !!( a[3] );
3150             var box = !!( a[2] );
3151
3152
3153             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3154                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3155             } else {
3156                 val = 0;
3157             }
3158
3159             return val;
3160         },
3161
3162
3163         getDefaultUnit: function(attr) {
3164             if (this.patterns.defaultUnit.test(attr)) {
3165                 return 'px';
3166             }
3167
3168             return '';
3169         },
3170
3171         animateX : function(callback, scope) {
3172             var f = function() {
3173                 this.onComplete.removeListener(f);
3174                 if (typeof callback == "function") {
3175                     callback.call(scope || this, this);
3176                 }
3177             };
3178             this.onComplete.addListener(f, this);
3179             this.animate();
3180         },
3181
3182
3183         setRuntimeAttribute: function(attr) {
3184             var start;
3185             var end;
3186             var attributes = this.attributes;
3187
3188             this.runtimeAttributes[attr] = {};
3189
3190             var isset = function(prop) {
3191                 return (typeof prop !== 'undefined');
3192             };
3193
3194             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3195                 return false;
3196             }
3197
3198             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3199
3200
3201             if (isset(attributes[attr]['to'])) {
3202                 end = attributes[attr]['to'];
3203             } else if (isset(attributes[attr]['by'])) {
3204                 if (start.constructor == Array) {
3205                     end = [];
3206                     for (var i = 0, len = start.length; i < len; ++i) {
3207                         end[i] = start[i] + attributes[attr]['by'][i];
3208                     }
3209                 } else {
3210                     end = start + attributes[attr]['by'];
3211                 }
3212             }
3213
3214             this.runtimeAttributes[attr].start = start;
3215             this.runtimeAttributes[attr].end = end;
3216
3217
3218             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3219         },
3220
3221
3222         init: function(el, attributes, duration, method) {
3223
3224             var isAnimated = false;
3225
3226
3227             var startTime = null;
3228
3229
3230             var actualFrames = 0;
3231
3232
3233             el = Roo.getDom(el);
3234
3235
3236             this.attributes = attributes || {};
3237
3238
3239             this.duration = duration || 1;
3240
3241
3242             this.method = method || Roo.lib.Easing.easeNone;
3243
3244
3245             this.useSeconds = true;
3246
3247
3248             this.currentFrame = 0;
3249
3250
3251             this.totalFrames = Roo.lib.AnimMgr.fps;
3252
3253
3254             this.getEl = function() {
3255                 return el;
3256             };
3257
3258
3259             this.isAnimated = function() {
3260                 return isAnimated;
3261             };
3262
3263
3264             this.getStartTime = function() {
3265                 return startTime;
3266             };
3267
3268             this.runtimeAttributes = {};
3269
3270
3271             this.animate = function() {
3272                 if (this.isAnimated()) {
3273                     return false;
3274                 }
3275
3276                 this.currentFrame = 0;
3277
3278                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3279
3280                 Roo.lib.AnimMgr.registerElement(this);
3281             };
3282
3283
3284             this.stop = function(finish) {
3285                 if (finish) {
3286                     this.currentFrame = this.totalFrames;
3287                     this._onTween.fire();
3288                 }
3289                 Roo.lib.AnimMgr.stop(this);
3290             };
3291
3292             var onStart = function() {
3293                 this.onStart.fire();
3294
3295                 this.runtimeAttributes = {};
3296                 for (var attr in this.attributes) {
3297                     this.setRuntimeAttribute(attr);
3298                 }
3299
3300                 isAnimated = true;
3301                 actualFrames = 0;
3302                 startTime = new Date();
3303             };
3304
3305
3306             var onTween = function() {
3307                 var data = {
3308                     duration: new Date() - this.getStartTime(),
3309                     currentFrame: this.currentFrame
3310                 };
3311
3312                 data.toString = function() {
3313                     return (
3314                             'duration: ' + data.duration +
3315                             ', currentFrame: ' + data.currentFrame
3316                             );
3317                 };
3318
3319                 this.onTween.fire(data);
3320
3321                 var runtimeAttributes = this.runtimeAttributes;
3322
3323                 for (var attr in runtimeAttributes) {
3324                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3325                 }
3326
3327                 actualFrames += 1;
3328             };
3329
3330             var onComplete = function() {
3331                 var actual_duration = (new Date() - startTime) / 1000 ;
3332
3333                 var data = {
3334                     duration: actual_duration,
3335                     frames: actualFrames,
3336                     fps: actualFrames / actual_duration
3337                 };
3338
3339                 data.toString = function() {
3340                     return (
3341                             'duration: ' + data.duration +
3342                             ', frames: ' + data.frames +
3343                             ', fps: ' + data.fps
3344                             );
3345                 };
3346
3347                 isAnimated = false;
3348                 actualFrames = 0;
3349                 this.onComplete.fire(data);
3350             };
3351
3352
3353             this._onStart = new Roo.util.Event(this);
3354             this.onStart = new Roo.util.Event(this);
3355             this.onTween = new Roo.util.Event(this);
3356             this._onTween = new Roo.util.Event(this);
3357             this.onComplete = new Roo.util.Event(this);
3358             this._onComplete = new Roo.util.Event(this);
3359             this._onStart.addListener(onStart);
3360             this._onTween.addListener(onTween);
3361             this._onComplete.addListener(onComplete);
3362         }
3363     };
3364 })();
3365 /*
3366  * Portions of this file are based on pieces of Yahoo User Interface Library
3367  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3368  * YUI licensed under the BSD License:
3369  * http://developer.yahoo.net/yui/license.txt
3370  * <script type="text/javascript">
3371  *
3372  */
3373
3374 Roo.lib.AnimMgr = new function() {
3375
3376         var thread = null;
3377
3378
3379         var queue = [];
3380
3381
3382         var tweenCount = 0;
3383
3384
3385         this.fps = 1000;
3386
3387
3388         this.delay = 1;
3389
3390
3391         this.registerElement = function(tween) {
3392             queue[queue.length] = tween;
3393             tweenCount += 1;
3394             tween._onStart.fire();
3395             this.start();
3396         };
3397
3398
3399         this.unRegister = function(tween, index) {
3400             tween._onComplete.fire();
3401             index = index || getIndex(tween);
3402             if (index != -1) {
3403                 queue.splice(index, 1);
3404             }
3405
3406             tweenCount -= 1;
3407             if (tweenCount <= 0) {
3408                 this.stop();
3409             }
3410         };
3411
3412
3413         this.start = function() {
3414             if (thread === null) {
3415                 thread = setInterval(this.run, this.delay);
3416             }
3417         };
3418
3419
3420         this.stop = function(tween) {
3421             if (!tween) {
3422                 clearInterval(thread);
3423
3424                 for (var i = 0, len = queue.length; i < len; ++i) {
3425                     if (queue[0].isAnimated()) {
3426                         this.unRegister(queue[0], 0);
3427                     }
3428                 }
3429
3430                 queue = [];
3431                 thread = null;
3432                 tweenCount = 0;
3433             }
3434             else {
3435                 this.unRegister(tween);
3436             }
3437         };
3438
3439
3440         this.run = function() {
3441             for (var i = 0, len = queue.length; i < len; ++i) {
3442                 var tween = queue[i];
3443                 if (!tween || !tween.isAnimated()) {
3444                     continue;
3445                 }
3446
3447                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3448                 {
3449                     tween.currentFrame += 1;
3450
3451                     if (tween.useSeconds) {
3452                         correctFrame(tween);
3453                     }
3454                     tween._onTween.fire();
3455                 }
3456                 else {
3457                     Roo.lib.AnimMgr.stop(tween, i);
3458                 }
3459             }
3460         };
3461
3462         var getIndex = function(anim) {
3463             for (var i = 0, len = queue.length; i < len; ++i) {
3464                 if (queue[i] == anim) {
3465                     return i;
3466                 }
3467             }
3468             return -1;
3469         };
3470
3471
3472         var correctFrame = function(tween) {
3473             var frames = tween.totalFrames;
3474             var frame = tween.currentFrame;
3475             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3476             var elapsed = (new Date() - tween.getStartTime());
3477             var tweak = 0;
3478
3479             if (elapsed < tween.duration * 1000) {
3480                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3481             } else {
3482                 tweak = frames - (frame + 1);
3483             }
3484             if (tweak > 0 && isFinite(tweak)) {
3485                 if (tween.currentFrame + tweak >= frames) {
3486                     tweak = frames - (frame + 1);
3487                 }
3488
3489                 tween.currentFrame += tweak;
3490             }
3491         };
3492     };/*
3493  * Portions of this file are based on pieces of Yahoo User Interface Library
3494  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3495  * YUI licensed under the BSD License:
3496  * http://developer.yahoo.net/yui/license.txt
3497  * <script type="text/javascript">
3498  *
3499  */
3500 Roo.lib.Bezier = new function() {
3501
3502         this.getPosition = function(points, t) {
3503             var n = points.length;
3504             var tmp = [];
3505
3506             for (var i = 0; i < n; ++i) {
3507                 tmp[i] = [points[i][0], points[i][1]];
3508             }
3509
3510             for (var j = 1; j < n; ++j) {
3511                 for (i = 0; i < n - j; ++i) {
3512                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3513                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3514                 }
3515             }
3516
3517             return [ tmp[0][0], tmp[0][1] ];
3518
3519         };
3520     };/*
3521  * Portions of this file are based on pieces of Yahoo User Interface Library
3522  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3523  * YUI licensed under the BSD License:
3524  * http://developer.yahoo.net/yui/license.txt
3525  * <script type="text/javascript">
3526  *
3527  */
3528 (function() {
3529
3530     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3531         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3532     };
3533
3534     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3535
3536     var fly = Roo.lib.AnimBase.fly;
3537     var Y = Roo.lib;
3538     var superclass = Y.ColorAnim.superclass;
3539     var proto = Y.ColorAnim.prototype;
3540
3541     proto.toString = function() {
3542         var el = this.getEl();
3543         var id = el.id || el.tagName;
3544         return ("ColorAnim " + id);
3545     };
3546
3547     proto.patterns.color = /color$/i;
3548     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3549     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3550     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3551     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3552
3553
3554     proto.parseColor = function(s) {
3555         if (s.length == 3) {
3556             return s;
3557         }
3558
3559         var c = this.patterns.hex.exec(s);
3560         if (c && c.length == 4) {
3561             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3562         }
3563
3564         c = this.patterns.rgb.exec(s);
3565         if (c && c.length == 4) {
3566             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3567         }
3568
3569         c = this.patterns.hex3.exec(s);
3570         if (c && c.length == 4) {
3571             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3572         }
3573
3574         return null;
3575     };
3576     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3577     proto.getAttribute = function(attr) {
3578         var el = this.getEl();
3579         if (this.patterns.color.test(attr)) {
3580             var val = fly(el).getStyle(attr);
3581
3582             if (this.patterns.transparent.test(val)) {
3583                 var parent = el.parentNode;
3584                 val = fly(parent).getStyle(attr);
3585
3586                 while (parent && this.patterns.transparent.test(val)) {
3587                     parent = parent.parentNode;
3588                     val = fly(parent).getStyle(attr);
3589                     if (parent.tagName.toUpperCase() == 'HTML') {
3590                         val = '#fff';
3591                     }
3592                 }
3593             }
3594         } else {
3595             val = superclass.getAttribute.call(this, attr);
3596         }
3597
3598         return val;
3599     };
3600     proto.getAttribute = function(attr) {
3601         var el = this.getEl();
3602         if (this.patterns.color.test(attr)) {
3603             var val = fly(el).getStyle(attr);
3604
3605             if (this.patterns.transparent.test(val)) {
3606                 var parent = el.parentNode;
3607                 val = fly(parent).getStyle(attr);
3608
3609                 while (parent && this.patterns.transparent.test(val)) {
3610                     parent = parent.parentNode;
3611                     val = fly(parent).getStyle(attr);
3612                     if (parent.tagName.toUpperCase() == 'HTML') {
3613                         val = '#fff';
3614                     }
3615                 }
3616             }
3617         } else {
3618             val = superclass.getAttribute.call(this, attr);
3619         }
3620
3621         return val;
3622     };
3623
3624     proto.doMethod = function(attr, start, end) {
3625         var val;
3626
3627         if (this.patterns.color.test(attr)) {
3628             val = [];
3629             for (var i = 0, len = start.length; i < len; ++i) {
3630                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3631             }
3632
3633             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3634         }
3635         else {
3636             val = superclass.doMethod.call(this, attr, start, end);
3637         }
3638
3639         return val;
3640     };
3641
3642     proto.setRuntimeAttribute = function(attr) {
3643         superclass.setRuntimeAttribute.call(this, attr);
3644
3645         if (this.patterns.color.test(attr)) {
3646             var attributes = this.attributes;
3647             var start = this.parseColor(this.runtimeAttributes[attr].start);
3648             var end = this.parseColor(this.runtimeAttributes[attr].end);
3649
3650             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3651                 end = this.parseColor(attributes[attr].by);
3652
3653                 for (var i = 0, len = start.length; i < len; ++i) {
3654                     end[i] = start[i] + end[i];
3655                 }
3656             }
3657
3658             this.runtimeAttributes[attr].start = start;
3659             this.runtimeAttributes[attr].end = end;
3660         }
3661     };
3662 })();
3663
3664 /*
3665  * Portions of this file are based on pieces of Yahoo User Interface Library
3666  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3667  * YUI licensed under the BSD License:
3668  * http://developer.yahoo.net/yui/license.txt
3669  * <script type="text/javascript">
3670  *
3671  */
3672 Roo.lib.Easing = {
3673
3674
3675     easeNone: function (t, b, c, d) {
3676         return c * t / d + b;
3677     },
3678
3679
3680     easeIn: function (t, b, c, d) {
3681         return c * (t /= d) * t + b;
3682     },
3683
3684
3685     easeOut: function (t, b, c, d) {
3686         return -c * (t /= d) * (t - 2) + b;
3687     },
3688
3689
3690     easeBoth: function (t, b, c, d) {
3691         if ((t /= d / 2) < 1) {
3692             return c / 2 * t * t + b;
3693         }
3694
3695         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3696     },
3697
3698
3699     easeInStrong: function (t, b, c, d) {
3700         return c * (t /= d) * t * t * t + b;
3701     },
3702
3703
3704     easeOutStrong: function (t, b, c, d) {
3705         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3706     },
3707
3708
3709     easeBothStrong: function (t, b, c, d) {
3710         if ((t /= d / 2) < 1) {
3711             return c / 2 * t * t * t * t + b;
3712         }
3713
3714         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3715     },
3716
3717
3718
3719     elasticIn: function (t, b, c, d, a, p) {
3720         if (t == 0) {
3721             return b;
3722         }
3723         if ((t /= d) == 1) {
3724             return b + c;
3725         }
3726         if (!p) {
3727             p = d * .3;
3728         }
3729
3730         if (!a || a < Math.abs(c)) {
3731             a = c;
3732             var s = p / 4;
3733         }
3734         else {
3735             var s = p / (2 * Math.PI) * Math.asin(c / a);
3736         }
3737
3738         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3739     },
3740
3741
3742     elasticOut: function (t, b, c, d, a, p) {
3743         if (t == 0) {
3744             return b;
3745         }
3746         if ((t /= d) == 1) {
3747             return b + c;
3748         }
3749         if (!p) {
3750             p = d * .3;
3751         }
3752
3753         if (!a || a < Math.abs(c)) {
3754             a = c;
3755             var s = p / 4;
3756         }
3757         else {
3758             var s = p / (2 * Math.PI) * Math.asin(c / a);
3759         }
3760
3761         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3762     },
3763
3764
3765     elasticBoth: function (t, b, c, d, a, p) {
3766         if (t == 0) {
3767             return b;
3768         }
3769
3770         if ((t /= d / 2) == 2) {
3771             return b + c;
3772         }
3773
3774         if (!p) {
3775             p = d * (.3 * 1.5);
3776         }
3777
3778         if (!a || a < Math.abs(c)) {
3779             a = c;
3780             var s = p / 4;
3781         }
3782         else {
3783             var s = p / (2 * Math.PI) * Math.asin(c / a);
3784         }
3785
3786         if (t < 1) {
3787             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3788                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3789         }
3790         return a * Math.pow(2, -10 * (t -= 1)) *
3791                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3792     },
3793
3794
3795
3796     backIn: function (t, b, c, d, s) {
3797         if (typeof s == 'undefined') {
3798             s = 1.70158;
3799         }
3800         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3801     },
3802
3803
3804     backOut: function (t, b, c, d, s) {
3805         if (typeof s == 'undefined') {
3806             s = 1.70158;
3807         }
3808         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3809     },
3810
3811
3812     backBoth: function (t, b, c, d, s) {
3813         if (typeof s == 'undefined') {
3814             s = 1.70158;
3815         }
3816
3817         if ((t /= d / 2 ) < 1) {
3818             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3819         }
3820         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3821     },
3822
3823
3824     bounceIn: function (t, b, c, d) {
3825         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3826     },
3827
3828
3829     bounceOut: function (t, b, c, d) {
3830         if ((t /= d) < (1 / 2.75)) {
3831             return c * (7.5625 * t * t) + b;
3832         } else if (t < (2 / 2.75)) {
3833             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3834         } else if (t < (2.5 / 2.75)) {
3835             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3836         }
3837         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3838     },
3839
3840
3841     bounceBoth: function (t, b, c, d) {
3842         if (t < d / 2) {
3843             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3844         }
3845         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3846     }
3847 };/*
3848  * Portions of this file are based on pieces of Yahoo User Interface Library
3849  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3850  * YUI licensed under the BSD License:
3851  * http://developer.yahoo.net/yui/license.txt
3852  * <script type="text/javascript">
3853  *
3854  */
3855     (function() {
3856         Roo.lib.Motion = function(el, attributes, duration, method) {
3857             if (el) {
3858                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3859             }
3860         };
3861
3862         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3863
3864
3865         var Y = Roo.lib;
3866         var superclass = Y.Motion.superclass;
3867         var proto = Y.Motion.prototype;
3868
3869         proto.toString = function() {
3870             var el = this.getEl();
3871             var id = el.id || el.tagName;
3872             return ("Motion " + id);
3873         };
3874
3875         proto.patterns.points = /^points$/i;
3876
3877         proto.setAttribute = function(attr, val, unit) {
3878             if (this.patterns.points.test(attr)) {
3879                 unit = unit || 'px';
3880                 superclass.setAttribute.call(this, 'left', val[0], unit);
3881                 superclass.setAttribute.call(this, 'top', val[1], unit);
3882             } else {
3883                 superclass.setAttribute.call(this, attr, val, unit);
3884             }
3885         };
3886
3887         proto.getAttribute = function(attr) {
3888             if (this.patterns.points.test(attr)) {
3889                 var val = [
3890                         superclass.getAttribute.call(this, 'left'),
3891                         superclass.getAttribute.call(this, 'top')
3892                         ];
3893             } else {
3894                 val = superclass.getAttribute.call(this, attr);
3895             }
3896
3897             return val;
3898         };
3899
3900         proto.doMethod = function(attr, start, end) {
3901             var val = null;
3902
3903             if (this.patterns.points.test(attr)) {
3904                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3905                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3906             } else {
3907                 val = superclass.doMethod.call(this, attr, start, end);
3908             }
3909             return val;
3910         };
3911
3912         proto.setRuntimeAttribute = function(attr) {
3913             if (this.patterns.points.test(attr)) {
3914                 var el = this.getEl();
3915                 var attributes = this.attributes;
3916                 var start;
3917                 var control = attributes['points']['control'] || [];
3918                 var end;
3919                 var i, len;
3920
3921                 if (control.length > 0 && !(control[0] instanceof Array)) {
3922                     control = [control];
3923                 } else {
3924                     var tmp = [];
3925                     for (i = 0,len = control.length; i < len; ++i) {
3926                         tmp[i] = control[i];
3927                     }
3928                     control = tmp;
3929                 }
3930
3931                 Roo.fly(el).position();
3932
3933                 if (isset(attributes['points']['from'])) {
3934                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3935                 }
3936                 else {
3937                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3938                 }
3939
3940                 start = this.getAttribute('points');
3941
3942
3943                 if (isset(attributes['points']['to'])) {
3944                     end = translateValues.call(this, attributes['points']['to'], start);
3945
3946                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3947                     for (i = 0,len = control.length; i < len; ++i) {
3948                         control[i] = translateValues.call(this, control[i], start);
3949                     }
3950
3951
3952                 } else if (isset(attributes['points']['by'])) {
3953                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3954
3955                     for (i = 0,len = control.length; i < len; ++i) {
3956                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3957                     }
3958                 }
3959
3960                 this.runtimeAttributes[attr] = [start];
3961
3962                 if (control.length > 0) {
3963                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3964                 }
3965
3966                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3967             }
3968             else {
3969                 superclass.setRuntimeAttribute.call(this, attr);
3970             }
3971         };
3972
3973         var translateValues = function(val, start) {
3974             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3975             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3976
3977             return val;
3978         };
3979
3980         var isset = function(prop) {
3981             return (typeof prop !== 'undefined');
3982         };
3983     })();
3984 /*
3985  * Portions of this file are based on pieces of Yahoo User Interface Library
3986  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3987  * YUI licensed under the BSD License:
3988  * http://developer.yahoo.net/yui/license.txt
3989  * <script type="text/javascript">
3990  *
3991  */
3992     (function() {
3993         Roo.lib.Scroll = function(el, attributes, duration, method) {
3994             if (el) {
3995                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3996             }
3997         };
3998
3999         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4000
4001
4002         var Y = Roo.lib;
4003         var superclass = Y.Scroll.superclass;
4004         var proto = Y.Scroll.prototype;
4005
4006         proto.toString = function() {
4007             var el = this.getEl();
4008             var id = el.id || el.tagName;
4009             return ("Scroll " + id);
4010         };
4011
4012         proto.doMethod = function(attr, start, end) {
4013             var val = null;
4014
4015             if (attr == 'scroll') {
4016                 val = [
4017                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4018                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4019                         ];
4020
4021             } else {
4022                 val = superclass.doMethod.call(this, attr, start, end);
4023             }
4024             return val;
4025         };
4026
4027         proto.getAttribute = function(attr) {
4028             var val = null;
4029             var el = this.getEl();
4030
4031             if (attr == 'scroll') {
4032                 val = [ el.scrollLeft, el.scrollTop ];
4033             } else {
4034                 val = superclass.getAttribute.call(this, attr);
4035             }
4036
4037             return val;
4038         };
4039
4040         proto.setAttribute = function(attr, val, unit) {
4041             var el = this.getEl();
4042
4043             if (attr == 'scroll') {
4044                 el.scrollLeft = val[0];
4045                 el.scrollTop = val[1];
4046             } else {
4047                 superclass.setAttribute.call(this, attr, val, unit);
4048             }
4049         };
4050     })();
4051 /*
4052  * Based on:
4053  * Ext JS Library 1.1.1
4054  * Copyright(c) 2006-2007, Ext JS, LLC.
4055  *
4056  * Originally Released Under LGPL - original licence link has changed is not relivant.
4057  *
4058  * Fork - LGPL
4059  * <script type="text/javascript">
4060  */
4061
4062
4063 // nasty IE9 hack - what a pile of crap that is..
4064
4065  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4066     Range.prototype.createContextualFragment = function (html) {
4067         var doc = window.document;
4068         var container = doc.createElement("div");
4069         container.innerHTML = html;
4070         var frag = doc.createDocumentFragment(), n;
4071         while ((n = container.firstChild)) {
4072             frag.appendChild(n);
4073         }
4074         return frag;
4075     };
4076 }
4077
4078 /**
4079  * @class Roo.DomHelper
4080  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4081  * For more information see <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4082  * @singleton
4083  */
4084 Roo.DomHelper = function(){
4085     var tempTableEl = null;
4086     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4087     var tableRe = /^table|tbody|tr|td$/i;
4088     var xmlns = {};
4089     // build as innerHTML where available
4090     /** @ignore */
4091     var createHtml = function(o){
4092         if(typeof o == 'string'){
4093             return o;
4094         }
4095         var b = "";
4096         if(!o.tag){
4097             o.tag = "div";
4098         }
4099         b += "<" + o.tag;
4100         for(var attr in o){
4101             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4102             if(attr == "style"){
4103                 var s = o["style"];
4104                 if(typeof s == "function"){
4105                     s = s.call();
4106                 }
4107                 if(typeof s == "string"){
4108                     b += ' style="' + s + '"';
4109                 }else if(typeof s == "object"){
4110                     b += ' style="';
4111                     for(var key in s){
4112                         if(typeof s[key] != "function"){
4113                             b += key + ":" + s[key] + ";";
4114                         }
4115                     }
4116                     b += '"';
4117                 }
4118             }else{
4119                 if(attr == "cls"){
4120                     b += ' class="' + o["cls"] + '"';
4121                 }else if(attr == "htmlFor"){
4122                     b += ' for="' + o["htmlFor"] + '"';
4123                 }else{
4124                     b += " " + attr + '="' + o[attr] + '"';
4125                 }
4126             }
4127         }
4128         if(emptyTags.test(o.tag)){
4129             b += "/>";
4130         }else{
4131             b += ">";
4132             var cn = o.children || o.cn;
4133             if(cn){
4134                 //http://bugs.kde.org/show_bug.cgi?id=71506
4135                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4136                     for(var i = 0, len = cn.length; i < len; i++) {
4137                         b += createHtml(cn[i], b);
4138                     }
4139                 }else{
4140                     b += createHtml(cn, b);
4141                 }
4142             }
4143             if(o.html){
4144                 b += o.html;
4145             }
4146             b += "</" + o.tag + ">";
4147         }
4148         return b;
4149     };
4150
4151     // build as dom
4152     /** @ignore */
4153     var createDom = function(o, parentNode){
4154          
4155         // defininition craeted..
4156         var ns = false;
4157         if (o.ns && o.ns != 'html') {
4158                
4159             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4160                 xmlns[o.ns] = o.xmlns;
4161                 ns = o.xmlns;
4162             }
4163             if (typeof(xmlns[o.ns]) == 'undefined') {
4164                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4165             }
4166             ns = xmlns[o.ns];
4167         }
4168         
4169         
4170         if (typeof(o) == 'string') {
4171             return parentNode.appendChild(document.createTextNode(o));
4172         }
4173         o.tag = o.tag || div;
4174         if (o.ns && Roo.isIE) {
4175             ns = false;
4176             o.tag = o.ns + ':' + o.tag;
4177             
4178         }
4179         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4180         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4181         for(var attr in o){
4182             
4183             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4184                     attr == "style" || typeof o[attr] == "function") continue;
4185                     
4186             if(attr=="cls" && Roo.isIE){
4187                 el.className = o["cls"];
4188             }else{
4189                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4190                 else el[attr] = o[attr];
4191             }
4192         }
4193         Roo.DomHelper.applyStyles(el, o.style);
4194         var cn = o.children || o.cn;
4195         if(cn){
4196             //http://bugs.kde.org/show_bug.cgi?id=71506
4197              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4198                 for(var i = 0, len = cn.length; i < len; i++) {
4199                     createDom(cn[i], el);
4200                 }
4201             }else{
4202                 createDom(cn, el);
4203             }
4204         }
4205         if(o.html){
4206             el.innerHTML = o.html;
4207         }
4208         if(parentNode){
4209            parentNode.appendChild(el);
4210         }
4211         return el;
4212     };
4213
4214     var ieTable = function(depth, s, h, e){
4215         tempTableEl.innerHTML = [s, h, e].join('');
4216         var i = -1, el = tempTableEl;
4217         while(++i < depth){
4218             el = el.firstChild;
4219         }
4220         return el;
4221     };
4222
4223     // kill repeat to save bytes
4224     var ts = '<table>',
4225         te = '</table>',
4226         tbs = ts+'<tbody>',
4227         tbe = '</tbody>'+te,
4228         trs = tbs + '<tr>',
4229         tre = '</tr>'+tbe;
4230
4231     /**
4232      * @ignore
4233      * Nasty code for IE's broken table implementation
4234      */
4235     var insertIntoTable = function(tag, where, el, html){
4236         if(!tempTableEl){
4237             tempTableEl = document.createElement('div');
4238         }
4239         var node;
4240         var before = null;
4241         if(tag == 'td'){
4242             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4243                 return;
4244             }
4245             if(where == 'beforebegin'){
4246                 before = el;
4247                 el = el.parentNode;
4248             } else{
4249                 before = el.nextSibling;
4250                 el = el.parentNode;
4251             }
4252             node = ieTable(4, trs, html, tre);
4253         }
4254         else if(tag == 'tr'){
4255             if(where == 'beforebegin'){
4256                 before = el;
4257                 el = el.parentNode;
4258                 node = ieTable(3, tbs, html, tbe);
4259             } else if(where == 'afterend'){
4260                 before = el.nextSibling;
4261                 el = el.parentNode;
4262                 node = ieTable(3, tbs, html, tbe);
4263             } else{ // INTO a TR
4264                 if(where == 'afterbegin'){
4265                     before = el.firstChild;
4266                 }
4267                 node = ieTable(4, trs, html, tre);
4268             }
4269         } else if(tag == 'tbody'){
4270             if(where == 'beforebegin'){
4271                 before = el;
4272                 el = el.parentNode;
4273                 node = ieTable(2, ts, html, te);
4274             } else if(where == 'afterend'){
4275                 before = el.nextSibling;
4276                 el = el.parentNode;
4277                 node = ieTable(2, ts, html, te);
4278             } else{
4279                 if(where == 'afterbegin'){
4280                     before = el.firstChild;
4281                 }
4282                 node = ieTable(3, tbs, html, tbe);
4283             }
4284         } else{ // TABLE
4285             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4286                 return;
4287             }
4288             if(where == 'afterbegin'){
4289                 before = el.firstChild;
4290             }
4291             node = ieTable(2, ts, html, te);
4292         }
4293         el.insertBefore(node, before);
4294         return node;
4295     };
4296
4297     return {
4298     /** True to force the use of DOM instead of html fragments @type Boolean */
4299     useDom : false,
4300
4301     /**
4302      * Returns the markup for the passed Element(s) config
4303      * @param {Object} o The Dom object spec (and children)
4304      * @return {String}
4305      */
4306     markup : function(o){
4307         return createHtml(o);
4308     },
4309
4310     /**
4311      * Applies a style specification to an element
4312      * @param {String/HTMLElement} el The element to apply styles to
4313      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4314      * a function which returns such a specification.
4315      */
4316     applyStyles : function(el, styles){
4317         if(styles){
4318            el = Roo.fly(el);
4319            if(typeof styles == "string"){
4320                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4321                var matches;
4322                while ((matches = re.exec(styles)) != null){
4323                    el.setStyle(matches[1], matches[2]);
4324                }
4325            }else if (typeof styles == "object"){
4326                for (var style in styles){
4327                   el.setStyle(style, styles[style]);
4328                }
4329            }else if (typeof styles == "function"){
4330                 Roo.DomHelper.applyStyles(el, styles.call());
4331            }
4332         }
4333     },
4334
4335     /**
4336      * Inserts an HTML fragment into the Dom
4337      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4338      * @param {HTMLElement} el The context element
4339      * @param {String} html The HTML fragmenet
4340      * @return {HTMLElement} The new node
4341      */
4342     insertHtml : function(where, el, html){
4343         where = where.toLowerCase();
4344         if(el.insertAdjacentHTML){
4345             if(tableRe.test(el.tagName)){
4346                 var rs;
4347                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4348                     return rs;
4349                 }
4350             }
4351             switch(where){
4352                 case "beforebegin":
4353                     el.insertAdjacentHTML('BeforeBegin', html);
4354                     return el.previousSibling;
4355                 case "afterbegin":
4356                     el.insertAdjacentHTML('AfterBegin', html);
4357                     return el.firstChild;
4358                 case "beforeend":
4359                     el.insertAdjacentHTML('BeforeEnd', html);
4360                     return el.lastChild;
4361                 case "afterend":
4362                     el.insertAdjacentHTML('AfterEnd', html);
4363                     return el.nextSibling;
4364             }
4365             throw 'Illegal insertion point -> "' + where + '"';
4366         }
4367         var range = el.ownerDocument.createRange();
4368         var frag;
4369         switch(where){
4370              case "beforebegin":
4371                 range.setStartBefore(el);
4372                 frag = range.createContextualFragment(html);
4373                 el.parentNode.insertBefore(frag, el);
4374                 return el.previousSibling;
4375              case "afterbegin":
4376                 if(el.firstChild){
4377                     range.setStartBefore(el.firstChild);
4378                     frag = range.createContextualFragment(html);
4379                     el.insertBefore(frag, el.firstChild);
4380                     return el.firstChild;
4381                 }else{
4382                     el.innerHTML = html;
4383                     return el.firstChild;
4384                 }
4385             case "beforeend":
4386                 if(el.lastChild){
4387                     range.setStartAfter(el.lastChild);
4388                     frag = range.createContextualFragment(html);
4389                     el.appendChild(frag);
4390                     return el.lastChild;
4391                 }else{
4392                     el.innerHTML = html;
4393                     return el.lastChild;
4394                 }
4395             case "afterend":
4396                 range.setStartAfter(el);
4397                 frag = range.createContextualFragment(html);
4398                 el.parentNode.insertBefore(frag, el.nextSibling);
4399                 return el.nextSibling;
4400             }
4401             throw 'Illegal insertion point -> "' + where + '"';
4402     },
4403
4404     /**
4405      * Creates new Dom element(s) and inserts them before el
4406      * @param {String/HTMLElement/Element} el The context element
4407      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4408      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4409      * @return {HTMLElement/Roo.Element} The new node
4410      */
4411     insertBefore : function(el, o, returnElement){
4412         return this.doInsert(el, o, returnElement, "beforeBegin");
4413     },
4414
4415     /**
4416      * Creates new Dom element(s) and inserts them after el
4417      * @param {String/HTMLElement/Element} el The context element
4418      * @param {Object} o The Dom object spec (and children)
4419      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4420      * @return {HTMLElement/Roo.Element} The new node
4421      */
4422     insertAfter : function(el, o, returnElement){
4423         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4424     },
4425
4426     /**
4427      * Creates new Dom element(s) and inserts them as the first child of el
4428      * @param {String/HTMLElement/Element} el The context element
4429      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4430      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4431      * @return {HTMLElement/Roo.Element} The new node
4432      */
4433     insertFirst : function(el, o, returnElement){
4434         return this.doInsert(el, o, returnElement, "afterBegin");
4435     },
4436
4437     // private
4438     doInsert : function(el, o, returnElement, pos, sibling){
4439         el = Roo.getDom(el);
4440         var newNode;
4441         if(this.useDom || o.ns){
4442             newNode = createDom(o, null);
4443             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4444         }else{
4445             var html = createHtml(o);
4446             newNode = this.insertHtml(pos, el, html);
4447         }
4448         return returnElement ? Roo.get(newNode, true) : newNode;
4449     },
4450
4451     /**
4452      * Creates new Dom element(s) and appends them to el
4453      * @param {String/HTMLElement/Element} el The context element
4454      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4455      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4456      * @return {HTMLElement/Roo.Element} The new node
4457      */
4458     append : function(el, o, returnElement){
4459         el = Roo.getDom(el);
4460         var newNode;
4461         if(this.useDom || o.ns){
4462             newNode = createDom(o, null);
4463             el.appendChild(newNode);
4464         }else{
4465             var html = createHtml(o);
4466             newNode = this.insertHtml("beforeEnd", el, html);
4467         }
4468         return returnElement ? Roo.get(newNode, true) : newNode;
4469     },
4470
4471     /**
4472      * Creates new Dom element(s) and overwrites the contents of el with them
4473      * @param {String/HTMLElement/Element} el The context element
4474      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4475      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4476      * @return {HTMLElement/Roo.Element} The new node
4477      */
4478     overwrite : function(el, o, returnElement){
4479         el = Roo.getDom(el);
4480         if (o.ns) {
4481           
4482             while (el.childNodes.length) {
4483                 el.removeChild(el.firstChild);
4484             }
4485             createDom(o, el);
4486         } else {
4487             el.innerHTML = createHtml(o);   
4488         }
4489         
4490         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4491     },
4492
4493     /**
4494      * Creates a new Roo.DomHelper.Template from the Dom object spec
4495      * @param {Object} o The Dom object spec (and children)
4496      * @return {Roo.DomHelper.Template} The new template
4497      */
4498     createTemplate : function(o){
4499         var html = createHtml(o);
4500         return new Roo.Template(html);
4501     }
4502     };
4503 }();
4504 /*
4505  * Based on:
4506  * Ext JS Library 1.1.1
4507  * Copyright(c) 2006-2007, Ext JS, LLC.
4508  *
4509  * Originally Released Under LGPL - original licence link has changed is not relivant.
4510  *
4511  * Fork - LGPL
4512  * <script type="text/javascript">
4513  */
4514  
4515 /**
4516 * @class Roo.Template
4517 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4518 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4519 * Usage:
4520 <pre><code>
4521 var t = new Roo.Template({
4522     html :  '&lt;div name="{id}"&gt;' + 
4523         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4524         '&lt;/div&gt;',
4525     myformat: function (value, allValues) {
4526         return 'XX' + value;
4527     }
4528 });
4529 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4530 </code></pre>
4531 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4532 * @constructor
4533 * @param {Object} cfg - Configuration object.
4534 */
4535 Roo.Template = function(cfg){
4536     // BC!
4537     if(cfg instanceof Array){
4538         cfg = cfg.join("");
4539     }else if(arguments.length > 1){
4540         cfg = Array.prototype.join.call(arguments, "");
4541     }
4542     
4543     
4544     if (typeof(cfg) == 'object') {
4545         Roo.apply(this,cfg)
4546     } else {
4547         // bc
4548         this.html = cfg;
4549     }
4550     
4551     
4552 };
4553 Roo.Template.prototype = {
4554     
4555     /**
4556      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4557      */
4558     html : '',
4559     /**
4560      * Returns an HTML fragment of this template with the specified values applied.
4561      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4562      * @return {String} The HTML fragment
4563      */
4564     applyTemplate : function(values){
4565         try {
4566             
4567             if(this.compiled){
4568                 return this.compiled(values);
4569             }
4570             var useF = this.disableFormats !== true;
4571             var fm = Roo.util.Format, tpl = this;
4572             var fn = function(m, name, format, args){
4573                 if(format && useF){
4574                     if(format.substr(0, 5) == "this."){
4575                         return tpl.call(format.substr(5), values[name], values);
4576                     }else{
4577                         if(args){
4578                             // quoted values are required for strings in compiled templates, 
4579                             // but for non compiled we need to strip them
4580                             // quoted reversed for jsmin
4581                             var re = /^\s*['"](.*)["']\s*$/;
4582                             args = args.split(',');
4583                             for(var i = 0, len = args.length; i < len; i++){
4584                                 args[i] = args[i].replace(re, "$1");
4585                             }
4586                             args = [values[name]].concat(args);
4587                         }else{
4588                             args = [values[name]];
4589                         }
4590                         return fm[format].apply(fm, args);
4591                     }
4592                 }else{
4593                     return values[name] !== undefined ? values[name] : "";
4594                 }
4595             };
4596             return this.html.replace(this.re, fn);
4597         } catch (e) {
4598             Roo.log(e);
4599             throw e;
4600         }
4601          
4602     },
4603     
4604     /**
4605      * Sets the HTML used as the template and optionally compiles it.
4606      * @param {String} html
4607      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4608      * @return {Roo.Template} this
4609      */
4610     set : function(html, compile){
4611         this.html = html;
4612         this.compiled = null;
4613         if(compile){
4614             this.compile();
4615         }
4616         return this;
4617     },
4618     
4619     /**
4620      * True to disable format functions (defaults to false)
4621      * @type Boolean
4622      */
4623     disableFormats : false,
4624     
4625     /**
4626     * The regular expression used to match template variables 
4627     * @type RegExp
4628     * @property 
4629     */
4630     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4631     
4632     /**
4633      * Compiles the template into an internal function, eliminating the RegEx overhead.
4634      * @return {Roo.Template} this
4635      */
4636     compile : function(){
4637         var fm = Roo.util.Format;
4638         var useF = this.disableFormats !== true;
4639         var sep = Roo.isGecko ? "+" : ",";
4640         var fn = function(m, name, format, args){
4641             if(format && useF){
4642                 args = args ? ',' + args : "";
4643                 if(format.substr(0, 5) != "this."){
4644                     format = "fm." + format + '(';
4645                 }else{
4646                     format = 'this.call("'+ format.substr(5) + '", ';
4647                     args = ", values";
4648                 }
4649             }else{
4650                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4651             }
4652             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4653         };
4654         var body;
4655         // branched to use + in gecko and [].join() in others
4656         if(Roo.isGecko){
4657             body = "this.compiled = function(values){ return '" +
4658                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4659                     "';};";
4660         }else{
4661             body = ["this.compiled = function(values){ return ['"];
4662             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4663             body.push("'].join('');};");
4664             body = body.join('');
4665         }
4666         /**
4667          * eval:var:values
4668          * eval:var:fm
4669          */
4670         eval(body);
4671         return this;
4672     },
4673     
4674     // private function used to call members
4675     call : function(fnName, value, allValues){
4676         return this[fnName](value, allValues);
4677     },
4678     
4679     /**
4680      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4681      * @param {String/HTMLElement/Roo.Element} el The context element
4682      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4683      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4684      * @return {HTMLElement/Roo.Element} The new node or Element
4685      */
4686     insertFirst: function(el, values, returnElement){
4687         return this.doInsert('afterBegin', el, values, returnElement);
4688     },
4689
4690     /**
4691      * Applies the supplied values to the template and inserts the new node(s) before el.
4692      * @param {String/HTMLElement/Roo.Element} el The context element
4693      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4694      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4695      * @return {HTMLElement/Roo.Element} The new node or Element
4696      */
4697     insertBefore: function(el, values, returnElement){
4698         return this.doInsert('beforeBegin', el, values, returnElement);
4699     },
4700
4701     /**
4702      * Applies the supplied values to the template and inserts the new node(s) after el.
4703      * @param {String/HTMLElement/Roo.Element} el The context element
4704      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4705      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4706      * @return {HTMLElement/Roo.Element} The new node or Element
4707      */
4708     insertAfter : function(el, values, returnElement){
4709         return this.doInsert('afterEnd', el, values, returnElement);
4710     },
4711     
4712     /**
4713      * Applies the supplied values to the template and appends the new node(s) to el.
4714      * @param {String/HTMLElement/Roo.Element} el The context element
4715      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4716      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4717      * @return {HTMLElement/Roo.Element} The new node or Element
4718      */
4719     append : function(el, values, returnElement){
4720         return this.doInsert('beforeEnd', el, values, returnElement);
4721     },
4722
4723     doInsert : function(where, el, values, returnEl){
4724         el = Roo.getDom(el);
4725         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4726         return returnEl ? Roo.get(newNode, true) : newNode;
4727     },
4728
4729     /**
4730      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4731      * @param {String/HTMLElement/Roo.Element} el The context element
4732      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4733      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4734      * @return {HTMLElement/Roo.Element} The new node or Element
4735      */
4736     overwrite : function(el, values, returnElement){
4737         el = Roo.getDom(el);
4738         el.innerHTML = this.applyTemplate(values);
4739         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4740     }
4741 };
4742 /**
4743  * Alias for {@link #applyTemplate}
4744  * @method
4745  */
4746 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4747
4748 // backwards compat
4749 Roo.DomHelper.Template = Roo.Template;
4750
4751 /**
4752  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4753  * @param {String/HTMLElement} el A DOM element or its id
4754  * @returns {Roo.Template} The created template
4755  * @static
4756  */
4757 Roo.Template.from = function(el){
4758     el = Roo.getDom(el);
4759     return new Roo.Template(el.value || el.innerHTML);
4760 };/*
4761  * Based on:
4762  * Ext JS Library 1.1.1
4763  * Copyright(c) 2006-2007, Ext JS, LLC.
4764  *
4765  * Originally Released Under LGPL - original licence link has changed is not relivant.
4766  *
4767  * Fork - LGPL
4768  * <script type="text/javascript">
4769  */
4770  
4771
4772 /*
4773  * This is code is also distributed under MIT license for use
4774  * with jQuery and prototype JavaScript libraries.
4775  */
4776 /**
4777  * @class Roo.DomQuery
4778 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4779 <p>
4780 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4781
4782 <p>
4783 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4784 </p>
4785 <h4>Element Selectors:</h4>
4786 <ul class="list">
4787     <li> <b>*</b> any element</li>
4788     <li> <b>E</b> an element with the tag E</li>
4789     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4790     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4791     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4792     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4793 </ul>
4794 <h4>Attribute Selectors:</h4>
4795 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4796 <ul class="list">
4797     <li> <b>E[foo]</b> has an attribute "foo"</li>
4798     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4799     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4800     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4801     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4802     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4803     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4804 </ul>
4805 <h4>Pseudo Classes:</h4>
4806 <ul class="list">
4807     <li> <b>E:first-child</b> E is the first child of its parent</li>
4808     <li> <b>E:last-child</b> E is the last child of its parent</li>
4809     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4810     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4811     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4812     <li> <b>E:only-child</b> E is the only child of its parent</li>
4813     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4814     <li> <b>E:first</b> the first E in the resultset</li>
4815     <li> <b>E:last</b> the last E in the resultset</li>
4816     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4817     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4818     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4819     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4820     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4821     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4822     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4823     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4824     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4825 </ul>
4826 <h4>CSS Value Selectors:</h4>
4827 <ul class="list">
4828     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4829     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4830     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4831     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4832     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4833     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4834 </ul>
4835  * @singleton
4836  */
4837 Roo.DomQuery = function(){
4838     var cache = {}, simpleCache = {}, valueCache = {};
4839     var nonSpace = /\S/;
4840     var trimRe = /^\s+|\s+$/g;
4841     var tplRe = /\{(\d+)\}/g;
4842     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4843     var tagTokenRe = /^(#)?([\w-\*]+)/;
4844     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4845
4846     function child(p, index){
4847         var i = 0;
4848         var n = p.firstChild;
4849         while(n){
4850             if(n.nodeType == 1){
4851                if(++i == index){
4852                    return n;
4853                }
4854             }
4855             n = n.nextSibling;
4856         }
4857         return null;
4858     };
4859
4860     function next(n){
4861         while((n = n.nextSibling) && n.nodeType != 1);
4862         return n;
4863     };
4864
4865     function prev(n){
4866         while((n = n.previousSibling) && n.nodeType != 1);
4867         return n;
4868     };
4869
4870     function children(d){
4871         var n = d.firstChild, ni = -1;
4872             while(n){
4873                 var nx = n.nextSibling;
4874                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4875                     d.removeChild(n);
4876                 }else{
4877                     n.nodeIndex = ++ni;
4878                 }
4879                 n = nx;
4880             }
4881             return this;
4882         };
4883
4884     function byClassName(c, a, v){
4885         if(!v){
4886             return c;
4887         }
4888         var r = [], ri = -1, cn;
4889         for(var i = 0, ci; ci = c[i]; i++){
4890             if((' '+ci.className+' ').indexOf(v) != -1){
4891                 r[++ri] = ci;
4892             }
4893         }
4894         return r;
4895     };
4896
4897     function attrValue(n, attr){
4898         if(!n.tagName && typeof n.length != "undefined"){
4899             n = n[0];
4900         }
4901         if(!n){
4902             return null;
4903         }
4904         if(attr == "for"){
4905             return n.htmlFor;
4906         }
4907         if(attr == "class" || attr == "className"){
4908             return n.className;
4909         }
4910         return n.getAttribute(attr) || n[attr];
4911
4912     };
4913
4914     function getNodes(ns, mode, tagName){
4915         var result = [], ri = -1, cs;
4916         if(!ns){
4917             return result;
4918         }
4919         tagName = tagName || "*";
4920         if(typeof ns.getElementsByTagName != "undefined"){
4921             ns = [ns];
4922         }
4923         if(!mode){
4924             for(var i = 0, ni; ni = ns[i]; i++){
4925                 cs = ni.getElementsByTagName(tagName);
4926                 for(var j = 0, ci; ci = cs[j]; j++){
4927                     result[++ri] = ci;
4928                 }
4929             }
4930         }else if(mode == "/" || mode == ">"){
4931             var utag = tagName.toUpperCase();
4932             for(var i = 0, ni, cn; ni = ns[i]; i++){
4933                 cn = ni.children || ni.childNodes;
4934                 for(var j = 0, cj; cj = cn[j]; j++){
4935                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4936                         result[++ri] = cj;
4937                     }
4938                 }
4939             }
4940         }else if(mode == "+"){
4941             var utag = tagName.toUpperCase();
4942             for(var i = 0, n; n = ns[i]; i++){
4943                 while((n = n.nextSibling) && n.nodeType != 1);
4944                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4945                     result[++ri] = n;
4946                 }
4947             }
4948         }else if(mode == "~"){
4949             for(var i = 0, n; n = ns[i]; i++){
4950                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4951                 if(n){
4952                     result[++ri] = n;
4953                 }
4954             }
4955         }
4956         return result;
4957     };
4958
4959     function concat(a, b){
4960         if(b.slice){
4961             return a.concat(b);
4962         }
4963         for(var i = 0, l = b.length; i < l; i++){
4964             a[a.length] = b[i];
4965         }
4966         return a;
4967     }
4968
4969     function byTag(cs, tagName){
4970         if(cs.tagName || cs == document){
4971             cs = [cs];
4972         }
4973         if(!tagName){
4974             return cs;
4975         }
4976         var r = [], ri = -1;
4977         tagName = tagName.toLowerCase();
4978         for(var i = 0, ci; ci = cs[i]; i++){
4979             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4980                 r[++ri] = ci;
4981             }
4982         }
4983         return r;
4984     };
4985
4986     function byId(cs, attr, id){
4987         if(cs.tagName || cs == document){
4988             cs = [cs];
4989         }
4990         if(!id){
4991             return cs;
4992         }
4993         var r = [], ri = -1;
4994         for(var i = 0,ci; ci = cs[i]; i++){
4995             if(ci && ci.id == id){
4996                 r[++ri] = ci;
4997                 return r;
4998             }
4999         }
5000         return r;
5001     };
5002
5003     function byAttribute(cs, attr, value, op, custom){
5004         var r = [], ri = -1, st = custom=="{";
5005         var f = Roo.DomQuery.operators[op];
5006         for(var i = 0, ci; ci = cs[i]; i++){
5007             var a;
5008             if(st){
5009                 a = Roo.DomQuery.getStyle(ci, attr);
5010             }
5011             else if(attr == "class" || attr == "className"){
5012                 a = ci.className;
5013             }else if(attr == "for"){
5014                 a = ci.htmlFor;
5015             }else if(attr == "href"){
5016                 a = ci.getAttribute("href", 2);
5017             }else{
5018                 a = ci.getAttribute(attr);
5019             }
5020             if((f && f(a, value)) || (!f && a)){
5021                 r[++ri] = ci;
5022             }
5023         }
5024         return r;
5025     };
5026
5027     function byPseudo(cs, name, value){
5028         return Roo.DomQuery.pseudos[name](cs, value);
5029     };
5030
5031     // This is for IE MSXML which does not support expandos.
5032     // IE runs the same speed using setAttribute, however FF slows way down
5033     // and Safari completely fails so they need to continue to use expandos.
5034     var isIE = window.ActiveXObject ? true : false;
5035
5036     // this eval is stop the compressor from
5037     // renaming the variable to something shorter
5038     
5039     /** eval:var:batch */
5040     var batch = 30803; 
5041
5042     var key = 30803;
5043
5044     function nodupIEXml(cs){
5045         var d = ++key;
5046         cs[0].setAttribute("_nodup", d);
5047         var r = [cs[0]];
5048         for(var i = 1, len = cs.length; i < len; i++){
5049             var c = cs[i];
5050             if(!c.getAttribute("_nodup") != d){
5051                 c.setAttribute("_nodup", d);
5052                 r[r.length] = c;
5053             }
5054         }
5055         for(var i = 0, len = cs.length; i < len; i++){
5056             cs[i].removeAttribute("_nodup");
5057         }
5058         return r;
5059     }
5060
5061     function nodup(cs){
5062         if(!cs){
5063             return [];
5064         }
5065         var len = cs.length, c, i, r = cs, cj, ri = -1;
5066         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5067             return cs;
5068         }
5069         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5070             return nodupIEXml(cs);
5071         }
5072         var d = ++key;
5073         cs[0]._nodup = d;
5074         for(i = 1; c = cs[i]; i++){
5075             if(c._nodup != d){
5076                 c._nodup = d;
5077             }else{
5078                 r = [];
5079                 for(var j = 0; j < i; j++){
5080                     r[++ri] = cs[j];
5081                 }
5082                 for(j = i+1; cj = cs[j]; j++){
5083                     if(cj._nodup != d){
5084                         cj._nodup = d;
5085                         r[++ri] = cj;
5086                     }
5087                 }
5088                 return r;
5089             }
5090         }
5091         return r;
5092     }
5093
5094     function quickDiffIEXml(c1, c2){
5095         var d = ++key;
5096         for(var i = 0, len = c1.length; i < len; i++){
5097             c1[i].setAttribute("_qdiff", d);
5098         }
5099         var r = [];
5100         for(var i = 0, len = c2.length; i < len; i++){
5101             if(c2[i].getAttribute("_qdiff") != d){
5102                 r[r.length] = c2[i];
5103             }
5104         }
5105         for(var i = 0, len = c1.length; i < len; i++){
5106            c1[i].removeAttribute("_qdiff");
5107         }
5108         return r;
5109     }
5110
5111     function quickDiff(c1, c2){
5112         var len1 = c1.length;
5113         if(!len1){
5114             return c2;
5115         }
5116         if(isIE && c1[0].selectSingleNode){
5117             return quickDiffIEXml(c1, c2);
5118         }
5119         var d = ++key;
5120         for(var i = 0; i < len1; i++){
5121             c1[i]._qdiff = d;
5122         }
5123         var r = [];
5124         for(var i = 0, len = c2.length; i < len; i++){
5125             if(c2[i]._qdiff != d){
5126                 r[r.length] = c2[i];
5127             }
5128         }
5129         return r;
5130     }
5131
5132     function quickId(ns, mode, root, id){
5133         if(ns == root){
5134            var d = root.ownerDocument || root;
5135            return d.getElementById(id);
5136         }
5137         ns = getNodes(ns, mode, "*");
5138         return byId(ns, null, id);
5139     }
5140
5141     return {
5142         getStyle : function(el, name){
5143             return Roo.fly(el).getStyle(name);
5144         },
5145         /**
5146          * Compiles a selector/xpath query into a reusable function. The returned function
5147          * takes one parameter "root" (optional), which is the context node from where the query should start.
5148          * @param {String} selector The selector/xpath query
5149          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5150          * @return {Function}
5151          */
5152         compile : function(path, type){
5153             type = type || "select";
5154             
5155             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5156             var q = path, mode, lq;
5157             var tk = Roo.DomQuery.matchers;
5158             var tklen = tk.length;
5159             var mm;
5160
5161             // accept leading mode switch
5162             var lmode = q.match(modeRe);
5163             if(lmode && lmode[1]){
5164                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5165                 q = q.replace(lmode[1], "");
5166             }
5167             // strip leading slashes
5168             while(path.substr(0, 1)=="/"){
5169                 path = path.substr(1);
5170             }
5171
5172             while(q && lq != q){
5173                 lq = q;
5174                 var tm = q.match(tagTokenRe);
5175                 if(type == "select"){
5176                     if(tm){
5177                         if(tm[1] == "#"){
5178                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5179                         }else{
5180                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5181                         }
5182                         q = q.replace(tm[0], "");
5183                     }else if(q.substr(0, 1) != '@'){
5184                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5185                     }
5186                 }else{
5187                     if(tm){
5188                         if(tm[1] == "#"){
5189                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5190                         }else{
5191                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5192                         }
5193                         q = q.replace(tm[0], "");
5194                     }
5195                 }
5196                 while(!(mm = q.match(modeRe))){
5197                     var matched = false;
5198                     for(var j = 0; j < tklen; j++){
5199                         var t = tk[j];
5200                         var m = q.match(t.re);
5201                         if(m){
5202                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5203                                                     return m[i];
5204                                                 });
5205                             q = q.replace(m[0], "");
5206                             matched = true;
5207                             break;
5208                         }
5209                     }
5210                     // prevent infinite loop on bad selector
5211                     if(!matched){
5212                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5213                     }
5214                 }
5215                 if(mm[1]){
5216                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5217                     q = q.replace(mm[1], "");
5218                 }
5219             }
5220             fn[fn.length] = "return nodup(n);\n}";
5221             
5222              /** 
5223               * list of variables that need from compression as they are used by eval.
5224              *  eval:var:batch 
5225              *  eval:var:nodup
5226              *  eval:var:byTag
5227              *  eval:var:ById
5228              *  eval:var:getNodes
5229              *  eval:var:quickId
5230              *  eval:var:mode
5231              *  eval:var:root
5232              *  eval:var:n
5233              *  eval:var:byClassName
5234              *  eval:var:byPseudo
5235              *  eval:var:byAttribute
5236              *  eval:var:attrValue
5237              * 
5238              **/ 
5239             eval(fn.join(""));
5240             return f;
5241         },
5242
5243         /**
5244          * Selects a group of elements.
5245          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5246          * @param {Node} root (optional) The start of the query (defaults to document).
5247          * @return {Array}
5248          */
5249         select : function(path, root, type){
5250             if(!root || root == document){
5251                 root = document;
5252             }
5253             if(typeof root == "string"){
5254                 root = document.getElementById(root);
5255             }
5256             var paths = path.split(",");
5257             var results = [];
5258             for(var i = 0, len = paths.length; i < len; i++){
5259                 var p = paths[i].replace(trimRe, "");
5260                 if(!cache[p]){
5261                     cache[p] = Roo.DomQuery.compile(p);
5262                     if(!cache[p]){
5263                         throw p + " is not a valid selector";
5264                     }
5265                 }
5266                 var result = cache[p](root);
5267                 if(result && result != document){
5268                     results = results.concat(result);
5269                 }
5270             }
5271             if(paths.length > 1){
5272                 return nodup(results);
5273             }
5274             return results;
5275         },
5276
5277         /**
5278          * Selects a single element.
5279          * @param {String} selector The selector/xpath query
5280          * @param {Node} root (optional) The start of the query (defaults to document).
5281          * @return {Element}
5282          */
5283         selectNode : function(path, root){
5284             return Roo.DomQuery.select(path, root)[0];
5285         },
5286
5287         /**
5288          * Selects the value of a node, optionally replacing null with the defaultValue.
5289          * @param {String} selector The selector/xpath query
5290          * @param {Node} root (optional) The start of the query (defaults to document).
5291          * @param {String} defaultValue
5292          */
5293         selectValue : function(path, root, defaultValue){
5294             path = path.replace(trimRe, "");
5295             if(!valueCache[path]){
5296                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5297             }
5298             var n = valueCache[path](root);
5299             n = n[0] ? n[0] : n;
5300             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5301             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5302         },
5303
5304         /**
5305          * Selects the value of a node, parsing integers and floats.
5306          * @param {String} selector The selector/xpath query
5307          * @param {Node} root (optional) The start of the query (defaults to document).
5308          * @param {Number} defaultValue
5309          * @return {Number}
5310          */
5311         selectNumber : function(path, root, defaultValue){
5312             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5313             return parseFloat(v);
5314         },
5315
5316         /**
5317          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5318          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5319          * @param {String} selector The simple selector to test
5320          * @return {Boolean}
5321          */
5322         is : function(el, ss){
5323             if(typeof el == "string"){
5324                 el = document.getElementById(el);
5325             }
5326             var isArray = (el instanceof Array);
5327             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5328             return isArray ? (result.length == el.length) : (result.length > 0);
5329         },
5330
5331         /**
5332          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5333          * @param {Array} el An array of elements to filter
5334          * @param {String} selector The simple selector to test
5335          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5336          * the selector instead of the ones that match
5337          * @return {Array}
5338          */
5339         filter : function(els, ss, nonMatches){
5340             ss = ss.replace(trimRe, "");
5341             if(!simpleCache[ss]){
5342                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5343             }
5344             var result = simpleCache[ss](els);
5345             return nonMatches ? quickDiff(result, els) : result;
5346         },
5347
5348         /**
5349          * Collection of matching regular expressions and code snippets.
5350          */
5351         matchers : [{
5352                 re: /^\.([\w-]+)/,
5353                 select: 'n = byClassName(n, null, " {1} ");'
5354             }, {
5355                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5356                 select: 'n = byPseudo(n, "{1}", "{2}");'
5357             },{
5358                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5359                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5360             }, {
5361                 re: /^#([\w-]+)/,
5362                 select: 'n = byId(n, null, "{1}");'
5363             },{
5364                 re: /^@([\w-]+)/,
5365                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5366             }
5367         ],
5368
5369         /**
5370          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5371          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5372          */
5373         operators : {
5374             "=" : function(a, v){
5375                 return a == v;
5376             },
5377             "!=" : function(a, v){
5378                 return a != v;
5379             },
5380             "^=" : function(a, v){
5381                 return a && a.substr(0, v.length) == v;
5382             },
5383             "$=" : function(a, v){
5384                 return a && a.substr(a.length-v.length) == v;
5385             },
5386             "*=" : function(a, v){
5387                 return a && a.indexOf(v) !== -1;
5388             },
5389             "%=" : function(a, v){
5390                 return (a % v) == 0;
5391             },
5392             "|=" : function(a, v){
5393                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5394             },
5395             "~=" : function(a, v){
5396                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5397             }
5398         },
5399
5400         /**
5401          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5402          * and the argument (if any) supplied in the selector.
5403          */
5404         pseudos : {
5405             "first-child" : function(c){
5406                 var r = [], ri = -1, n;
5407                 for(var i = 0, ci; ci = n = c[i]; i++){
5408                     while((n = n.previousSibling) && n.nodeType != 1);
5409                     if(!n){
5410                         r[++ri] = ci;
5411                     }
5412                 }
5413                 return r;
5414             },
5415
5416             "last-child" : function(c){
5417                 var r = [], ri = -1, n;
5418                 for(var i = 0, ci; ci = n = c[i]; i++){
5419                     while((n = n.nextSibling) && n.nodeType != 1);
5420                     if(!n){
5421                         r[++ri] = ci;
5422                     }
5423                 }
5424                 return r;
5425             },
5426
5427             "nth-child" : function(c, a) {
5428                 var r = [], ri = -1;
5429                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5430                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5431                 for(var i = 0, n; n = c[i]; i++){
5432                     var pn = n.parentNode;
5433                     if (batch != pn._batch) {
5434                         var j = 0;
5435                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5436                             if(cn.nodeType == 1){
5437                                cn.nodeIndex = ++j;
5438                             }
5439                         }
5440                         pn._batch = batch;
5441                     }
5442                     if (f == 1) {
5443                         if (l == 0 || n.nodeIndex == l){
5444                             r[++ri] = n;
5445                         }
5446                     } else if ((n.nodeIndex + l) % f == 0){
5447                         r[++ri] = n;
5448                     }
5449                 }
5450
5451                 return r;
5452             },
5453
5454             "only-child" : function(c){
5455                 var r = [], ri = -1;;
5456                 for(var i = 0, ci; ci = c[i]; i++){
5457                     if(!prev(ci) && !next(ci)){
5458                         r[++ri] = ci;
5459                     }
5460                 }
5461                 return r;
5462             },
5463
5464             "empty" : function(c){
5465                 var r = [], ri = -1;
5466                 for(var i = 0, ci; ci = c[i]; i++){
5467                     var cns = ci.childNodes, j = 0, cn, empty = true;
5468                     while(cn = cns[j]){
5469                         ++j;
5470                         if(cn.nodeType == 1 || cn.nodeType == 3){
5471                             empty = false;
5472                             break;
5473                         }
5474                     }
5475                     if(empty){
5476                         r[++ri] = ci;
5477                     }
5478                 }
5479                 return r;
5480             },
5481
5482             "contains" : function(c, v){
5483                 var r = [], ri = -1;
5484                 for(var i = 0, ci; ci = c[i]; i++){
5485                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5486                         r[++ri] = ci;
5487                     }
5488                 }
5489                 return r;
5490             },
5491
5492             "nodeValue" : function(c, v){
5493                 var r = [], ri = -1;
5494                 for(var i = 0, ci; ci = c[i]; i++){
5495                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5496                         r[++ri] = ci;
5497                     }
5498                 }
5499                 return r;
5500             },
5501
5502             "checked" : function(c){
5503                 var r = [], ri = -1;
5504                 for(var i = 0, ci; ci = c[i]; i++){
5505                     if(ci.checked == true){
5506                         r[++ri] = ci;
5507                     }
5508                 }
5509                 return r;
5510             },
5511
5512             "not" : function(c, ss){
5513                 return Roo.DomQuery.filter(c, ss, true);
5514             },
5515
5516             "odd" : function(c){
5517                 return this["nth-child"](c, "odd");
5518             },
5519
5520             "even" : function(c){
5521                 return this["nth-child"](c, "even");
5522             },
5523
5524             "nth" : function(c, a){
5525                 return c[a-1] || [];
5526             },
5527
5528             "first" : function(c){
5529                 return c[0] || [];
5530             },
5531
5532             "last" : function(c){
5533                 return c[c.length-1] || [];
5534             },
5535
5536             "has" : function(c, ss){
5537                 var s = Roo.DomQuery.select;
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     if(s(ss, ci).length > 0){
5541                         r[++ri] = ci;
5542                     }
5543                 }
5544                 return r;
5545             },
5546
5547             "next" : function(c, ss){
5548                 var is = Roo.DomQuery.is;
5549                 var r = [], ri = -1;
5550                 for(var i = 0, ci; ci = c[i]; i++){
5551                     var n = next(ci);
5552                     if(n && is(n, ss)){
5553                         r[++ri] = ci;
5554                     }
5555                 }
5556                 return r;
5557             },
5558
5559             "prev" : function(c, ss){
5560                 var is = Roo.DomQuery.is;
5561                 var r = [], ri = -1;
5562                 for(var i = 0, ci; ci = c[i]; i++){
5563                     var n = prev(ci);
5564                     if(n && is(n, ss)){
5565                         r[++ri] = ci;
5566                     }
5567                 }
5568                 return r;
5569             }
5570         }
5571     };
5572 }();
5573
5574 /**
5575  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5576  * @param {String} path The selector/xpath query
5577  * @param {Node} root (optional) The start of the query (defaults to document).
5578  * @return {Array}
5579  * @member Roo
5580  * @method query
5581  */
5582 Roo.query = Roo.DomQuery.select;
5583 /*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.util.Observable
5596  * Base class that provides a common interface for publishing events. Subclasses are expected to
5597  * to have a property "events" with all the events defined.<br>
5598  * For example:
5599  * <pre><code>
5600  Employee = function(name){
5601     this.name = name;
5602     this.addEvents({
5603         "fired" : true,
5604         "quit" : true
5605     });
5606  }
5607  Roo.extend(Employee, Roo.util.Observable);
5608 </code></pre>
5609  * @param {Object} config properties to use (incuding events / listeners)
5610  */
5611
5612 Roo.util.Observable = function(cfg){
5613     
5614     cfg = cfg|| {};
5615     this.addEvents(cfg.events || {});
5616     if (cfg.events) {
5617         delete cfg.events; // make sure
5618     }
5619      
5620     Roo.apply(this, cfg);
5621     
5622     if(this.listeners){
5623         this.on(this.listeners);
5624         delete this.listeners;
5625     }
5626 };
5627 Roo.util.Observable.prototype = {
5628     /** 
5629  * @cfg {Object} listeners  list of events and functions to call for this object, 
5630  * For example :
5631  * <pre><code>
5632     listeners :  { 
5633        'click' : function(e) {
5634            ..... 
5635         } ,
5636         .... 
5637     } 
5638   </code></pre>
5639  */
5640     
5641     
5642     /**
5643      * Fires the specified event with the passed parameters (minus the event name).
5644      * @param {String} eventName
5645      * @param {Object...} args Variable number of parameters are passed to handlers
5646      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5647      */
5648     fireEvent : function(){
5649         var ce = this.events[arguments[0].toLowerCase()];
5650         if(typeof ce == "object"){
5651             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5652         }else{
5653             return true;
5654         }
5655     },
5656
5657     // private
5658     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5659
5660     /**
5661      * Appends an event handler to this component
5662      * @param {String}   eventName The type of event to listen for
5663      * @param {Function} handler The method the event invokes
5664      * @param {Object}   scope (optional) The scope in which to execute the handler
5665      * function. The handler function's "this" context.
5666      * @param {Object}   options (optional) An object containing handler configuration
5667      * properties. This may contain any of the following properties:<ul>
5668      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5669      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5670      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5671      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5672      * by the specified number of milliseconds. If the event fires again within that time, the original
5673      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5674      * </ul><br>
5675      * <p>
5676      * <b>Combining Options</b><br>
5677      * Using the options argument, it is possible to combine different types of listeners:<br>
5678      * <br>
5679      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5680                 <pre><code>
5681                 el.on('click', this.onClick, this, {
5682                         single: true,
5683                 delay: 100,
5684                 forumId: 4
5685                 });
5686                 </code></pre>
5687      * <p>
5688      * <b>Attaching multiple handlers in 1 call</b><br>
5689      * The method also allows for a single argument to be passed which is a config object containing properties
5690      * which specify multiple handlers.
5691      * <pre><code>
5692                 el.on({
5693                         'click': {
5694                         fn: this.onClick,
5695                         scope: this,
5696                         delay: 100
5697                 }, 
5698                 'mouseover': {
5699                         fn: this.onMouseOver,
5700                         scope: this
5701                 },
5702                 'mouseout': {
5703                         fn: this.onMouseOut,
5704                         scope: this
5705                 }
5706                 });
5707                 </code></pre>
5708      * <p>
5709      * Or a shorthand syntax which passes the same scope object to all handlers:
5710         <pre><code>
5711                 el.on({
5712                         'click': this.onClick,
5713                 'mouseover': this.onMouseOver,
5714                 'mouseout': this.onMouseOut,
5715                 scope: this
5716                 });
5717                 </code></pre>
5718      */
5719     addListener : function(eventName, fn, scope, o){
5720         if(typeof eventName == "object"){
5721             o = eventName;
5722             for(var e in o){
5723                 if(this.filterOptRe.test(e)){
5724                     continue;
5725                 }
5726                 if(typeof o[e] == "function"){
5727                     // shared options
5728                     this.addListener(e, o[e], o.scope,  o);
5729                 }else{
5730                     // individual options
5731                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5732                 }
5733             }
5734             return;
5735         }
5736         o = (!o || typeof o == "boolean") ? {} : o;
5737         eventName = eventName.toLowerCase();
5738         var ce = this.events[eventName] || true;
5739         if(typeof ce == "boolean"){
5740             ce = new Roo.util.Event(this, eventName);
5741             this.events[eventName] = ce;
5742         }
5743         ce.addListener(fn, scope, o);
5744     },
5745
5746     /**
5747      * Removes a listener
5748      * @param {String}   eventName     The type of event to listen for
5749      * @param {Function} handler        The handler to remove
5750      * @param {Object}   scope  (optional) The scope (this object) for the handler
5751      */
5752     removeListener : function(eventName, fn, scope){
5753         var ce = this.events[eventName.toLowerCase()];
5754         if(typeof ce == "object"){
5755             ce.removeListener(fn, scope);
5756         }
5757     },
5758
5759     /**
5760      * Removes all listeners for this object
5761      */
5762     purgeListeners : function(){
5763         for(var evt in this.events){
5764             if(typeof this.events[evt] == "object"){
5765                  this.events[evt].clearListeners();
5766             }
5767         }
5768     },
5769
5770     relayEvents : function(o, events){
5771         var createHandler = function(ename){
5772             return function(){
5773                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5774             };
5775         };
5776         for(var i = 0, len = events.length; i < len; i++){
5777             var ename = events[i];
5778             if(!this.events[ename]){ this.events[ename] = true; };
5779             o.on(ename, createHandler(ename), this);
5780         }
5781     },
5782
5783     /**
5784      * Used to define events on this Observable
5785      * @param {Object} object The object with the events defined
5786      */
5787     addEvents : function(o){
5788         if(!this.events){
5789             this.events = {};
5790         }
5791         Roo.applyIf(this.events, o);
5792     },
5793
5794     /**
5795      * Checks to see if this object has any listeners for a specified event
5796      * @param {String} eventName The name of the event to check for
5797      * @return {Boolean} True if the event is being listened for, else false
5798      */
5799     hasListener : function(eventName){
5800         var e = this.events[eventName];
5801         return typeof e == "object" && e.listeners.length > 0;
5802     }
5803 };
5804 /**
5805  * Appends an event handler to this element (shorthand for addListener)
5806  * @param {String}   eventName     The type of event to listen for
5807  * @param {Function} handler        The method the event invokes
5808  * @param {Object}   scope (optional) The scope in which to execute the handler
5809  * function. The handler function's "this" context.
5810  * @param {Object}   options  (optional)
5811  * @method
5812  */
5813 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5814 /**
5815  * Removes a listener (shorthand for removeListener)
5816  * @param {String}   eventName     The type of event to listen for
5817  * @param {Function} handler        The handler to remove
5818  * @param {Object}   scope  (optional) The scope (this object) for the handler
5819  * @method
5820  */
5821 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5822
5823 /**
5824  * Starts capture on the specified Observable. All events will be passed
5825  * to the supplied function with the event name + standard signature of the event
5826  * <b>before</b> the event is fired. If the supplied function returns false,
5827  * the event will not fire.
5828  * @param {Observable} o The Observable to capture
5829  * @param {Function} fn The function to call
5830  * @param {Object} scope (optional) The scope (this object) for the fn
5831  * @static
5832  */
5833 Roo.util.Observable.capture = function(o, fn, scope){
5834     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5835 };
5836
5837 /**
5838  * Removes <b>all</b> added captures from the Observable.
5839  * @param {Observable} o The Observable to release
5840  * @static
5841  */
5842 Roo.util.Observable.releaseCapture = function(o){
5843     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5844 };
5845
5846 (function(){
5847
5848     var createBuffered = function(h, o, scope){
5849         var task = new Roo.util.DelayedTask();
5850         return function(){
5851             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5852         };
5853     };
5854
5855     var createSingle = function(h, e, fn, scope){
5856         return function(){
5857             e.removeListener(fn, scope);
5858             return h.apply(scope, arguments);
5859         };
5860     };
5861
5862     var createDelayed = function(h, o, scope){
5863         return function(){
5864             var args = Array.prototype.slice.call(arguments, 0);
5865             setTimeout(function(){
5866                 h.apply(scope, args);
5867             }, o.delay || 10);
5868         };
5869     };
5870
5871     Roo.util.Event = function(obj, name){
5872         this.name = name;
5873         this.obj = obj;
5874         this.listeners = [];
5875     };
5876
5877     Roo.util.Event.prototype = {
5878         addListener : function(fn, scope, options){
5879             var o = options || {};
5880             scope = scope || this.obj;
5881             if(!this.isListening(fn, scope)){
5882                 var l = {fn: fn, scope: scope, options: o};
5883                 var h = fn;
5884                 if(o.delay){
5885                     h = createDelayed(h, o, scope);
5886                 }
5887                 if(o.single){
5888                     h = createSingle(h, this, fn, scope);
5889                 }
5890                 if(o.buffer){
5891                     h = createBuffered(h, o, scope);
5892                 }
5893                 l.fireFn = h;
5894                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5895                     this.listeners.push(l);
5896                 }else{
5897                     this.listeners = this.listeners.slice(0);
5898                     this.listeners.push(l);
5899                 }
5900             }
5901         },
5902
5903         findListener : function(fn, scope){
5904             scope = scope || this.obj;
5905             var ls = this.listeners;
5906             for(var i = 0, len = ls.length; i < len; i++){
5907                 var l = ls[i];
5908                 if(l.fn == fn && l.scope == scope){
5909                     return i;
5910                 }
5911             }
5912             return -1;
5913         },
5914
5915         isListening : function(fn, scope){
5916             return this.findListener(fn, scope) != -1;
5917         },
5918
5919         removeListener : function(fn, scope){
5920             var index;
5921             if((index = this.findListener(fn, scope)) != -1){
5922                 if(!this.firing){
5923                     this.listeners.splice(index, 1);
5924                 }else{
5925                     this.listeners = this.listeners.slice(0);
5926                     this.listeners.splice(index, 1);
5927                 }
5928                 return true;
5929             }
5930             return false;
5931         },
5932
5933         clearListeners : function(){
5934             this.listeners = [];
5935         },
5936
5937         fire : function(){
5938             var ls = this.listeners, scope, len = ls.length;
5939             if(len > 0){
5940                 this.firing = true;
5941                 var args = Array.prototype.slice.call(arguments, 0);
5942                 for(var i = 0; i < len; i++){
5943                     var l = ls[i];
5944                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5945                         this.firing = false;
5946                         return false;
5947                     }
5948                 }
5949                 this.firing = false;
5950             }
5951             return true;
5952         }
5953     };
5954 })();/*
5955  * Based on:
5956  * Ext JS Library 1.1.1
5957  * Copyright(c) 2006-2007, Ext JS, LLC.
5958  *
5959  * Originally Released Under LGPL - original licence link has changed is not relivant.
5960  *
5961  * Fork - LGPL
5962  * <script type="text/javascript">
5963  */
5964
5965 /**
5966  * @class Roo.EventManager
5967  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5968  * several useful events directly.
5969  * See {@link Roo.EventObject} for more details on normalized event objects.
5970  * @singleton
5971  */
5972 Roo.EventManager = function(){
5973     var docReadyEvent, docReadyProcId, docReadyState = false;
5974     var resizeEvent, resizeTask, textEvent, textSize;
5975     var E = Roo.lib.Event;
5976     var D = Roo.lib.Dom;
5977
5978
5979     var fireDocReady = function(){
5980         if(!docReadyState){
5981             docReadyState = true;
5982             Roo.isReady = true;
5983             if(docReadyProcId){
5984                 clearInterval(docReadyProcId);
5985             }
5986             if(Roo.isGecko || Roo.isOpera) {
5987                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5988             }
5989             if(Roo.isIE){
5990                 var defer = document.getElementById("ie-deferred-loader");
5991                 if(defer){
5992                     defer.onreadystatechange = null;
5993                     defer.parentNode.removeChild(defer);
5994                 }
5995             }
5996             if(docReadyEvent){
5997                 docReadyEvent.fire();
5998                 docReadyEvent.clearListeners();
5999             }
6000         }
6001     };
6002     
6003     var initDocReady = function(){
6004         docReadyEvent = new Roo.util.Event();
6005         if(Roo.isGecko || Roo.isOpera) {
6006             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6007         }else if(Roo.isIE){
6008             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6009             var defer = document.getElementById("ie-deferred-loader");
6010             defer.onreadystatechange = function(){
6011                 if(this.readyState == "complete"){
6012                     fireDocReady();
6013                 }
6014             };
6015         }else if(Roo.isSafari){ 
6016             docReadyProcId = setInterval(function(){
6017                 var rs = document.readyState;
6018                 if(rs == "complete") {
6019                     fireDocReady();     
6020                  }
6021             }, 10);
6022         }
6023         // no matter what, make sure it fires on load
6024         E.on(window, "load", fireDocReady);
6025     };
6026
6027     var createBuffered = function(h, o){
6028         var task = new Roo.util.DelayedTask(h);
6029         return function(e){
6030             // create new event object impl so new events don't wipe out properties
6031             e = new Roo.EventObjectImpl(e);
6032             task.delay(o.buffer, h, null, [e]);
6033         };
6034     };
6035
6036     var createSingle = function(h, el, ename, fn){
6037         return function(e){
6038             Roo.EventManager.removeListener(el, ename, fn);
6039             h(e);
6040         };
6041     };
6042
6043     var createDelayed = function(h, o){
6044         return function(e){
6045             // create new event object impl so new events don't wipe out properties
6046             e = new Roo.EventObjectImpl(e);
6047             setTimeout(function(){
6048                 h(e);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     var listen = function(element, ename, opt, fn, scope){
6054         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6055         fn = fn || o.fn; scope = scope || o.scope;
6056         var el = Roo.getDom(element);
6057         if(!el){
6058             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6059         }
6060         var h = function(e){
6061             e = Roo.EventObject.setEvent(e);
6062             var t;
6063             if(o.delegate){
6064                 t = e.getTarget(o.delegate, el);
6065                 if(!t){
6066                     return;
6067                 }
6068             }else{
6069                 t = e.target;
6070             }
6071             if(o.stopEvent === true){
6072                 e.stopEvent();
6073             }
6074             if(o.preventDefault === true){
6075                e.preventDefault();
6076             }
6077             if(o.stopPropagation === true){
6078                 e.stopPropagation();
6079             }
6080
6081             if(o.normalized === false){
6082                 e = e.browserEvent;
6083             }
6084
6085             fn.call(scope || el, e, t, o);
6086         };
6087         if(o.delay){
6088             h = createDelayed(h, o);
6089         }
6090         if(o.single){
6091             h = createSingle(h, el, ename, fn);
6092         }
6093         if(o.buffer){
6094             h = createBuffered(h, o);
6095         }
6096         fn._handlers = fn._handlers || [];
6097         fn._handlers.push([Roo.id(el), ename, h]);
6098
6099         E.on(el, ename, h);
6100         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6101             el.addEventListener("DOMMouseScroll", h, false);
6102             E.on(window, 'unload', function(){
6103                 el.removeEventListener("DOMMouseScroll", h, false);
6104             });
6105         }
6106         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6107             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6108         }
6109         return h;
6110     };
6111
6112     var stopListening = function(el, ename, fn){
6113         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6114         if(hds){
6115             for(var i = 0, len = hds.length; i < len; i++){
6116                 var h = hds[i];
6117                 if(h[0] == id && h[1] == ename){
6118                     hd = h[2];
6119                     hds.splice(i, 1);
6120                     break;
6121                 }
6122             }
6123         }
6124         E.un(el, ename, hd);
6125         el = Roo.getDom(el);
6126         if(ename == "mousewheel" && el.addEventListener){
6127             el.removeEventListener("DOMMouseScroll", hd, false);
6128         }
6129         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6130             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6131         }
6132     };
6133
6134     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6135     
6136     var pub = {
6137         
6138         
6139         /** 
6140          * Fix for doc tools
6141          * @scope Roo.EventManager
6142          */
6143         
6144         
6145         /** 
6146          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6147          * object with a Roo.EventObject
6148          * @param {Function} fn        The method the event invokes
6149          * @param {Object}   scope    An object that becomes the scope of the handler
6150          * @param {boolean}  override If true, the obj passed in becomes
6151          *                             the execution scope of the listener
6152          * @return {Function} The wrapped function
6153          * @deprecated
6154          */
6155         wrap : function(fn, scope, override){
6156             return function(e){
6157                 Roo.EventObject.setEvent(e);
6158                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6159             };
6160         },
6161         
6162         /**
6163      * Appends an event handler to an element (shorthand for addListener)
6164      * @param {String/HTMLElement}   element        The html element or id to assign the
6165      * @param {String}   eventName The type of event to listen for
6166      * @param {Function} handler The method the event invokes
6167      * @param {Object}   scope (optional) The scope in which to execute the handler
6168      * function. The handler function's "this" context.
6169      * @param {Object}   options (optional) An object containing handler configuration
6170      * properties. This may contain any of the following properties:<ul>
6171      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6172      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6173      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6174      * <li>preventDefault {Boolean} True to prevent the default action</li>
6175      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6176      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6177      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6178      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6179      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6180      * by the specified number of milliseconds. If the event fires again within that time, the original
6181      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6182      * </ul><br>
6183      * <p>
6184      * <b>Combining Options</b><br>
6185      * Using the options argument, it is possible to combine different types of listeners:<br>
6186      * <br>
6187      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6188      * Code:<pre><code>
6189 el.on('click', this.onClick, this, {
6190     single: true,
6191     delay: 100,
6192     stopEvent : true,
6193     forumId: 4
6194 });</code></pre>
6195      * <p>
6196      * <b>Attaching multiple handlers in 1 call</b><br>
6197       * The method also allows for a single argument to be passed which is a config object containing properties
6198      * which specify multiple handlers.
6199      * <p>
6200      * Code:<pre><code>
6201 el.on({
6202     'click' : {
6203         fn: this.onClick
6204         scope: this,
6205         delay: 100
6206     },
6207     'mouseover' : {
6208         fn: this.onMouseOver
6209         scope: this
6210     },
6211     'mouseout' : {
6212         fn: this.onMouseOut
6213         scope: this
6214     }
6215 });</code></pre>
6216      * <p>
6217      * Or a shorthand syntax:<br>
6218      * Code:<pre><code>
6219 el.on({
6220     'click' : this.onClick,
6221     'mouseover' : this.onMouseOver,
6222     'mouseout' : this.onMouseOut
6223     scope: this
6224 });</code></pre>
6225      */
6226         addListener : function(element, eventName, fn, scope, options){
6227             if(typeof eventName == "object"){
6228                 var o = eventName;
6229                 for(var e in o){
6230                     if(propRe.test(e)){
6231                         continue;
6232                     }
6233                     if(typeof o[e] == "function"){
6234                         // shared options
6235                         listen(element, e, o, o[e], o.scope);
6236                     }else{
6237                         // individual options
6238                         listen(element, e, o[e]);
6239                     }
6240                 }
6241                 return;
6242             }
6243             return listen(element, eventName, options, fn, scope);
6244         },
6245         
6246         /**
6247          * Removes an event handler
6248          *
6249          * @param {String/HTMLElement}   element        The id or html element to remove the 
6250          *                             event from
6251          * @param {String}   eventName     The type of event
6252          * @param {Function} fn
6253          * @return {Boolean} True if a listener was actually removed
6254          */
6255         removeListener : function(element, eventName, fn){
6256             return stopListening(element, eventName, fn);
6257         },
6258         
6259         /**
6260          * Fires when the document is ready (before onload and before images are loaded). Can be 
6261          * accessed shorthanded Roo.onReady().
6262          * @param {Function} fn        The method the event invokes
6263          * @param {Object}   scope    An  object that becomes the scope of the handler
6264          * @param {boolean}  options
6265          */
6266         onDocumentReady : function(fn, scope, options){
6267             if(docReadyState){ // if it already fired
6268                 docReadyEvent.addListener(fn, scope, options);
6269                 docReadyEvent.fire();
6270                 docReadyEvent.clearListeners();
6271                 return;
6272             }
6273             if(!docReadyEvent){
6274                 initDocReady();
6275             }
6276             docReadyEvent.addListener(fn, scope, options);
6277         },
6278         
6279         /**
6280          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6281          * @param {Function} fn        The method the event invokes
6282          * @param {Object}   scope    An object that becomes the scope of the handler
6283          * @param {boolean}  options
6284          */
6285         onWindowResize : function(fn, scope, options){
6286             if(!resizeEvent){
6287                 resizeEvent = new Roo.util.Event();
6288                 resizeTask = new Roo.util.DelayedTask(function(){
6289                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6290                 });
6291                 E.on(window, "resize", function(){
6292                     if(Roo.isIE){
6293                         resizeTask.delay(50);
6294                     }else{
6295                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6296                     }
6297                 });
6298             }
6299             resizeEvent.addListener(fn, scope, options);
6300         },
6301
6302         /**
6303          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6304          * @param {Function} fn        The method the event invokes
6305          * @param {Object}   scope    An object that becomes the scope of the handler
6306          * @param {boolean}  options
6307          */
6308         onTextResize : function(fn, scope, options){
6309             if(!textEvent){
6310                 textEvent = new Roo.util.Event();
6311                 var textEl = new Roo.Element(document.createElement('div'));
6312                 textEl.dom.className = 'x-text-resize';
6313                 textEl.dom.innerHTML = 'X';
6314                 textEl.appendTo(document.body);
6315                 textSize = textEl.dom.offsetHeight;
6316                 setInterval(function(){
6317                     if(textEl.dom.offsetHeight != textSize){
6318                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6319                     }
6320                 }, this.textResizeInterval);
6321             }
6322             textEvent.addListener(fn, scope, options);
6323         },
6324
6325         /**
6326          * Removes the passed window resize listener.
6327          * @param {Function} fn        The method the event invokes
6328          * @param {Object}   scope    The scope of handler
6329          */
6330         removeResizeListener : function(fn, scope){
6331             if(resizeEvent){
6332                 resizeEvent.removeListener(fn, scope);
6333             }
6334         },
6335
6336         // private
6337         fireResize : function(){
6338             if(resizeEvent){
6339                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6340             }   
6341         },
6342         /**
6343          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6344          */
6345         ieDeferSrc : false,
6346         /**
6347          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6348          */
6349         textResizeInterval : 50
6350     };
6351     
6352     /**
6353      * Fix for doc tools
6354      * @scopeAlias pub=Roo.EventManager
6355      */
6356     
6357      /**
6358      * Appends an event handler to an element (shorthand for addListener)
6359      * @param {String/HTMLElement}   element        The html element or id to assign the
6360      * @param {String}   eventName The type of event to listen for
6361      * @param {Function} handler The method the event invokes
6362      * @param {Object}   scope (optional) The scope in which to execute the handler
6363      * function. The handler function's "this" context.
6364      * @param {Object}   options (optional) An object containing handler configuration
6365      * properties. This may contain any of the following properties:<ul>
6366      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6367      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6368      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6369      * <li>preventDefault {Boolean} True to prevent the default action</li>
6370      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6371      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6372      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6373      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6374      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6375      * by the specified number of milliseconds. If the event fires again within that time, the original
6376      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6377      * </ul><br>
6378      * <p>
6379      * <b>Combining Options</b><br>
6380      * Using the options argument, it is possible to combine different types of listeners:<br>
6381      * <br>
6382      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6383      * Code:<pre><code>
6384 el.on('click', this.onClick, this, {
6385     single: true,
6386     delay: 100,
6387     stopEvent : true,
6388     forumId: 4
6389 });</code></pre>
6390      * <p>
6391      * <b>Attaching multiple handlers in 1 call</b><br>
6392       * The method also allows for a single argument to be passed which is a config object containing properties
6393      * which specify multiple handlers.
6394      * <p>
6395      * Code:<pre><code>
6396 el.on({
6397     'click' : {
6398         fn: this.onClick
6399         scope: this,
6400         delay: 100
6401     },
6402     'mouseover' : {
6403         fn: this.onMouseOver
6404         scope: this
6405     },
6406     'mouseout' : {
6407         fn: this.onMouseOut
6408         scope: this
6409     }
6410 });</code></pre>
6411      * <p>
6412      * Or a shorthand syntax:<br>
6413      * Code:<pre><code>
6414 el.on({
6415     'click' : this.onClick,
6416     'mouseover' : this.onMouseOver,
6417     'mouseout' : this.onMouseOut
6418     scope: this
6419 });</code></pre>
6420      */
6421     pub.on = pub.addListener;
6422     pub.un = pub.removeListener;
6423
6424     pub.stoppedMouseDownEvent = new Roo.util.Event();
6425     return pub;
6426 }();
6427 /**
6428   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6429   * @param {Function} fn        The method the event invokes
6430   * @param {Object}   scope    An  object that becomes the scope of the handler
6431   * @param {boolean}  override If true, the obj passed in becomes
6432   *                             the execution scope of the listener
6433   * @member Roo
6434   * @method onReady
6435  */
6436 Roo.onReady = Roo.EventManager.onDocumentReady;
6437
6438 Roo.onReady(function(){
6439     var bd = Roo.get(document.body);
6440     if(!bd){ return; }
6441
6442     var cls = [
6443             Roo.isIE ? "roo-ie"
6444             : Roo.isGecko ? "roo-gecko"
6445             : Roo.isOpera ? "roo-opera"
6446             : Roo.isSafari ? "roo-safari" : ""];
6447
6448     if(Roo.isMac){
6449         cls.push("roo-mac");
6450     }
6451     if(Roo.isLinux){
6452         cls.push("roo-linux");
6453     }
6454     if(Roo.isBorderBox){
6455         cls.push('roo-border-box');
6456     }
6457     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6458         var p = bd.dom.parentNode;
6459         if(p){
6460             p.className += ' roo-strict';
6461         }
6462     }
6463     bd.addClass(cls.join(' '));
6464 });
6465
6466 /**
6467  * @class Roo.EventObject
6468  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6469  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6470  * Example:
6471  * <pre><code>
6472  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6473     e.preventDefault();
6474     var target = e.getTarget();
6475     ...
6476  }
6477  var myDiv = Roo.get("myDiv");
6478  myDiv.on("click", handleClick);
6479  //or
6480  Roo.EventManager.on("myDiv", 'click', handleClick);
6481  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6482  </code></pre>
6483  * @singleton
6484  */
6485 Roo.EventObject = function(){
6486     
6487     var E = Roo.lib.Event;
6488     
6489     // safari keypress events for special keys return bad keycodes
6490     var safariKeys = {
6491         63234 : 37, // left
6492         63235 : 39, // right
6493         63232 : 38, // up
6494         63233 : 40, // down
6495         63276 : 33, // page up
6496         63277 : 34, // page down
6497         63272 : 46, // delete
6498         63273 : 36, // home
6499         63275 : 35  // end
6500     };
6501
6502     // normalize button clicks
6503     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6504                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6505
6506     Roo.EventObjectImpl = function(e){
6507         if(e){
6508             this.setEvent(e.browserEvent || e);
6509         }
6510     };
6511     Roo.EventObjectImpl.prototype = {
6512         /**
6513          * Used to fix doc tools.
6514          * @scope Roo.EventObject.prototype
6515          */
6516             
6517
6518         
6519         
6520         /** The normal browser event */
6521         browserEvent : null,
6522         /** The button pressed in a mouse event */
6523         button : -1,
6524         /** True if the shift key was down during the event */
6525         shiftKey : false,
6526         /** True if the control key was down during the event */
6527         ctrlKey : false,
6528         /** True if the alt key was down during the event */
6529         altKey : false,
6530
6531         /** Key constant 
6532         * @type Number */
6533         BACKSPACE : 8,
6534         /** Key constant 
6535         * @type Number */
6536         TAB : 9,
6537         /** Key constant 
6538         * @type Number */
6539         RETURN : 13,
6540         /** Key constant 
6541         * @type Number */
6542         ENTER : 13,
6543         /** Key constant 
6544         * @type Number */
6545         SHIFT : 16,
6546         /** Key constant 
6547         * @type Number */
6548         CONTROL : 17,
6549         /** Key constant 
6550         * @type Number */
6551         ESC : 27,
6552         /** Key constant 
6553         * @type Number */
6554         SPACE : 32,
6555         /** Key constant 
6556         * @type Number */
6557         PAGEUP : 33,
6558         /** Key constant 
6559         * @type Number */
6560         PAGEDOWN : 34,
6561         /** Key constant 
6562         * @type Number */
6563         END : 35,
6564         /** Key constant 
6565         * @type Number */
6566         HOME : 36,
6567         /** Key constant 
6568         * @type Number */
6569         LEFT : 37,
6570         /** Key constant 
6571         * @type Number */
6572         UP : 38,
6573         /** Key constant 
6574         * @type Number */
6575         RIGHT : 39,
6576         /** Key constant 
6577         * @type Number */
6578         DOWN : 40,
6579         /** Key constant 
6580         * @type Number */
6581         DELETE : 46,
6582         /** Key constant 
6583         * @type Number */
6584         F5 : 116,
6585
6586            /** @private */
6587         setEvent : function(e){
6588             if(e == this || (e && e.browserEvent)){ // already wrapped
6589                 return e;
6590             }
6591             this.browserEvent = e;
6592             if(e){
6593                 // normalize buttons
6594                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6595                 if(e.type == 'click' && this.button == -1){
6596                     this.button = 0;
6597                 }
6598                 this.type = e.type;
6599                 this.shiftKey = e.shiftKey;
6600                 // mac metaKey behaves like ctrlKey
6601                 this.ctrlKey = e.ctrlKey || e.metaKey;
6602                 this.altKey = e.altKey;
6603                 // in getKey these will be normalized for the mac
6604                 this.keyCode = e.keyCode;
6605                 // keyup warnings on firefox.
6606                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6607                 // cache the target for the delayed and or buffered events
6608                 this.target = E.getTarget(e);
6609                 // same for XY
6610                 this.xy = E.getXY(e);
6611             }else{
6612                 this.button = -1;
6613                 this.shiftKey = false;
6614                 this.ctrlKey = false;
6615                 this.altKey = false;
6616                 this.keyCode = 0;
6617                 this.charCode =0;
6618                 this.target = null;
6619                 this.xy = [0, 0];
6620             }
6621             return this;
6622         },
6623
6624         /**
6625          * Stop the event (preventDefault and stopPropagation)
6626          */
6627         stopEvent : function(){
6628             if(this.browserEvent){
6629                 if(this.browserEvent.type == 'mousedown'){
6630                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6631                 }
6632                 E.stopEvent(this.browserEvent);
6633             }
6634         },
6635
6636         /**
6637          * Prevents the browsers default handling of the event.
6638          */
6639         preventDefault : function(){
6640             if(this.browserEvent){
6641                 E.preventDefault(this.browserEvent);
6642             }
6643         },
6644
6645         /** @private */
6646         isNavKeyPress : function(){
6647             var k = this.keyCode;
6648             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6649             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6650         },
6651
6652         isSpecialKey : function(){
6653             var k = this.keyCode;
6654             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6655             (k == 16) || (k == 17) ||
6656             (k >= 18 && k <= 20) ||
6657             (k >= 33 && k <= 35) ||
6658             (k >= 36 && k <= 39) ||
6659             (k >= 44 && k <= 45);
6660         },
6661         /**
6662          * Cancels bubbling of the event.
6663          */
6664         stopPropagation : function(){
6665             if(this.browserEvent){
6666                 if(this.type == 'mousedown'){
6667                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6668                 }
6669                 E.stopPropagation(this.browserEvent);
6670             }
6671         },
6672
6673         /**
6674          * Gets the key code for the event.
6675          * @return {Number}
6676          */
6677         getCharCode : function(){
6678             return this.charCode || this.keyCode;
6679         },
6680
6681         /**
6682          * Returns a normalized keyCode for the event.
6683          * @return {Number} The key code
6684          */
6685         getKey : function(){
6686             var k = this.keyCode || this.charCode;
6687             return Roo.isSafari ? (safariKeys[k] || k) : k;
6688         },
6689
6690         /**
6691          * Gets the x coordinate of the event.
6692          * @return {Number}
6693          */
6694         getPageX : function(){
6695             return this.xy[0];
6696         },
6697
6698         /**
6699          * Gets the y coordinate of the event.
6700          * @return {Number}
6701          */
6702         getPageY : function(){
6703             return this.xy[1];
6704         },
6705
6706         /**
6707          * Gets the time of the event.
6708          * @return {Number}
6709          */
6710         getTime : function(){
6711             if(this.browserEvent){
6712                 return E.getTime(this.browserEvent);
6713             }
6714             return null;
6715         },
6716
6717         /**
6718          * Gets the page coordinates of the event.
6719          * @return {Array} The xy values like [x, y]
6720          */
6721         getXY : function(){
6722             return this.xy;
6723         },
6724
6725         /**
6726          * Gets the target for the event.
6727          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6728          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6729                 search as a number or element (defaults to 10 || document.body)
6730          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6731          * @return {HTMLelement}
6732          */
6733         getTarget : function(selector, maxDepth, returnEl){
6734             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6735         },
6736         /**
6737          * Gets the related target.
6738          * @return {HTMLElement}
6739          */
6740         getRelatedTarget : function(){
6741             if(this.browserEvent){
6742                 return E.getRelatedTarget(this.browserEvent);
6743             }
6744             return null;
6745         },
6746
6747         /**
6748          * Normalizes mouse wheel delta across browsers
6749          * @return {Number} The delta
6750          */
6751         getWheelDelta : function(){
6752             var e = this.browserEvent;
6753             var delta = 0;
6754             if(e.wheelDelta){ /* IE/Opera. */
6755                 delta = e.wheelDelta/120;
6756             }else if(e.detail){ /* Mozilla case. */
6757                 delta = -e.detail/3;
6758             }
6759             return delta;
6760         },
6761
6762         /**
6763          * Returns true if the control, meta, shift or alt key was pressed during this event.
6764          * @return {Boolean}
6765          */
6766         hasModifier : function(){
6767             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6768         },
6769
6770         /**
6771          * Returns true if the target of this event equals el or is a child of el
6772          * @param {String/HTMLElement/Element} el
6773          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6774          * @return {Boolean}
6775          */
6776         within : function(el, related){
6777             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6778             return t && Roo.fly(el).contains(t);
6779         },
6780
6781         getPoint : function(){
6782             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6783         }
6784     };
6785
6786     return new Roo.EventObjectImpl();
6787 }();
6788             
6789     /*
6790  * Based on:
6791  * Ext JS Library 1.1.1
6792  * Copyright(c) 2006-2007, Ext JS, LLC.
6793  *
6794  * Originally Released Under LGPL - original licence link has changed is not relivant.
6795  *
6796  * Fork - LGPL
6797  * <script type="text/javascript">
6798  */
6799
6800  
6801 // was in Composite Element!??!?!
6802  
6803 (function(){
6804     var D = Roo.lib.Dom;
6805     var E = Roo.lib.Event;
6806     var A = Roo.lib.Anim;
6807
6808     // local style camelizing for speed
6809     var propCache = {};
6810     var camelRe = /(-[a-z])/gi;
6811     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6812     var view = document.defaultView;
6813
6814 /**
6815  * @class Roo.Element
6816  * Represents an Element in the DOM.<br><br>
6817  * Usage:<br>
6818 <pre><code>
6819 var el = Roo.get("my-div");
6820
6821 // or with getEl
6822 var el = getEl("my-div");
6823
6824 // or with a DOM element
6825 var el = Roo.get(myDivElement);
6826 </code></pre>
6827  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6828  * each call instead of constructing a new one.<br><br>
6829  * <b>Animations</b><br />
6830  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6831  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6832 <pre>
6833 Option    Default   Description
6834 --------- --------  ---------------------------------------------
6835 duration  .35       The duration of the animation in seconds
6836 easing    easeOut   The YUI easing method
6837 callback  none      A function to execute when the anim completes
6838 scope     this      The scope (this) of the callback function
6839 </pre>
6840 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6841 * manipulate the animation. Here's an example:
6842 <pre><code>
6843 var el = Roo.get("my-div");
6844
6845 // no animation
6846 el.setWidth(100);
6847
6848 // default animation
6849 el.setWidth(100, true);
6850
6851 // animation with some options set
6852 el.setWidth(100, {
6853     duration: 1,
6854     callback: this.foo,
6855     scope: this
6856 });
6857
6858 // using the "anim" property to get the Anim object
6859 var opt = {
6860     duration: 1,
6861     callback: this.foo,
6862     scope: this
6863 };
6864 el.setWidth(100, opt);
6865 ...
6866 if(opt.anim.isAnimated()){
6867     opt.anim.stop();
6868 }
6869 </code></pre>
6870 * <b> Composite (Collections of) Elements</b><br />
6871  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6872  * @constructor Create a new Element directly.
6873  * @param {String/HTMLElement} element
6874  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6875  */
6876     Roo.Element = function(element, forceNew){
6877         var dom = typeof element == "string" ?
6878                 document.getElementById(element) : element;
6879         if(!dom){ // invalid id/element
6880             return null;
6881         }
6882         var id = dom.id;
6883         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6884             return Roo.Element.cache[id];
6885         }
6886
6887         /**
6888          * The DOM element
6889          * @type HTMLElement
6890          */
6891         this.dom = dom;
6892
6893         /**
6894          * The DOM element ID
6895          * @type String
6896          */
6897         this.id = id || Roo.id(dom);
6898     };
6899
6900     var El = Roo.Element;
6901
6902     El.prototype = {
6903         /**
6904          * The element's default display mode  (defaults to "")
6905          * @type String
6906          */
6907         originalDisplay : "",
6908
6909         visibilityMode : 1,
6910         /**
6911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6912          * @type String
6913          */
6914         defaultUnit : "px",
6915         /**
6916          * Sets the element's visibility mode. When setVisible() is called it
6917          * will use this to determine whether to set the visibility or the display property.
6918          * @param visMode Element.VISIBILITY or Element.DISPLAY
6919          * @return {Roo.Element} this
6920          */
6921         setVisibilityMode : function(visMode){
6922             this.visibilityMode = visMode;
6923             return this;
6924         },
6925         /**
6926          * Convenience method for setVisibilityMode(Element.DISPLAY)
6927          * @param {String} display (optional) What to set display to when visible
6928          * @return {Roo.Element} this
6929          */
6930         enableDisplayMode : function(display){
6931             this.setVisibilityMode(El.DISPLAY);
6932             if(typeof display != "undefined") this.originalDisplay = display;
6933             return this;
6934         },
6935
6936         /**
6937          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6938          * @param {String} selector The simple selector to test
6939          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6940                 search as a number or element (defaults to 10 || document.body)
6941          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6942          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6943          */
6944         findParent : function(simpleSelector, maxDepth, returnEl){
6945             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6946             maxDepth = maxDepth || 50;
6947             if(typeof maxDepth != "number"){
6948                 stopEl = Roo.getDom(maxDepth);
6949                 maxDepth = 10;
6950             }
6951             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6952                 if(dq.is(p, simpleSelector)){
6953                     return returnEl ? Roo.get(p) : p;
6954                 }
6955                 depth++;
6956                 p = p.parentNode;
6957             }
6958             return null;
6959         },
6960
6961
6962         /**
6963          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6964          * @param {String} selector The simple selector to test
6965          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6966                 search as a number or element (defaults to 10 || document.body)
6967          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6968          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6969          */
6970         findParentNode : function(simpleSelector, maxDepth, returnEl){
6971             var p = Roo.fly(this.dom.parentNode, '_internal');
6972             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6973         },
6974
6975         /**
6976          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6977          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6978          * @param {String} selector The simple selector to test
6979          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6980                 search as a number or element (defaults to 10 || document.body)
6981          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6982          */
6983         up : function(simpleSelector, maxDepth){
6984             return this.findParentNode(simpleSelector, maxDepth, true);
6985         },
6986
6987
6988
6989         /**
6990          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6991          * @param {String} selector The simple selector to test
6992          * @return {Boolean} True if this element matches the selector, else false
6993          */
6994         is : function(simpleSelector){
6995             return Roo.DomQuery.is(this.dom, simpleSelector);
6996         },
6997
6998         /**
6999          * Perform animation on this element.
7000          * @param {Object} args The YUI animation control args
7001          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7002          * @param {Function} onComplete (optional) Function to call when animation completes
7003          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7004          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7005          * @return {Roo.Element} this
7006          */
7007         animate : function(args, duration, onComplete, easing, animType){
7008             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7009             return this;
7010         },
7011
7012         /*
7013          * @private Internal animation call
7014          */
7015         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7016             animType = animType || 'run';
7017             opt = opt || {};
7018             var anim = Roo.lib.Anim[animType](
7019                 this.dom, args,
7020                 (opt.duration || defaultDur) || .35,
7021                 (opt.easing || defaultEase) || 'easeOut',
7022                 function(){
7023                     Roo.callback(cb, this);
7024                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7025                 },
7026                 this
7027             );
7028             opt.anim = anim;
7029             return anim;
7030         },
7031
7032         // private legacy anim prep
7033         preanim : function(a, i){
7034             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7035         },
7036
7037         /**
7038          * Removes worthless text nodes
7039          * @param {Boolean} forceReclean (optional) By default the element
7040          * keeps track if it has been cleaned already so
7041          * you can call this over and over. However, if you update the element and
7042          * need to force a reclean, you can pass true.
7043          */
7044         clean : function(forceReclean){
7045             if(this.isCleaned && forceReclean !== true){
7046                 return this;
7047             }
7048             var ns = /\S/;
7049             var d = this.dom, n = d.firstChild, ni = -1;
7050             while(n){
7051                 var nx = n.nextSibling;
7052                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7053                     d.removeChild(n);
7054                 }else{
7055                     n.nodeIndex = ++ni;
7056                 }
7057                 n = nx;
7058             }
7059             this.isCleaned = true;
7060             return this;
7061         },
7062
7063         // private
7064         calcOffsetsTo : function(el){
7065             el = Roo.get(el);
7066             var d = el.dom;
7067             var restorePos = false;
7068             if(el.getStyle('position') == 'static'){
7069                 el.position('relative');
7070                 restorePos = true;
7071             }
7072             var x = 0, y =0;
7073             var op = this.dom;
7074             while(op && op != d && op.tagName != 'HTML'){
7075                 x+= op.offsetLeft;
7076                 y+= op.offsetTop;
7077                 op = op.offsetParent;
7078             }
7079             if(restorePos){
7080                 el.position('static');
7081             }
7082             return [x, y];
7083         },
7084
7085         /**
7086          * Scrolls this element into view within the passed container.
7087          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7088          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7089          * @return {Roo.Element} this
7090          */
7091         scrollIntoView : function(container, hscroll){
7092             var c = Roo.getDom(container) || document.body;
7093             var el = this.dom;
7094
7095             var o = this.calcOffsetsTo(c),
7096                 l = o[0],
7097                 t = o[1],
7098                 b = t+el.offsetHeight,
7099                 r = l+el.offsetWidth;
7100
7101             var ch = c.clientHeight;
7102             var ct = parseInt(c.scrollTop, 10);
7103             var cl = parseInt(c.scrollLeft, 10);
7104             var cb = ct + ch;
7105             var cr = cl + c.clientWidth;
7106
7107             if(t < ct){
7108                 c.scrollTop = t;
7109             }else if(b > cb){
7110                 c.scrollTop = b-ch;
7111             }
7112
7113             if(hscroll !== false){
7114                 if(l < cl){
7115                     c.scrollLeft = l;
7116                 }else if(r > cr){
7117                     c.scrollLeft = r-c.clientWidth;
7118                 }
7119             }
7120             return this;
7121         },
7122
7123         // private
7124         scrollChildIntoView : function(child, hscroll){
7125             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7126         },
7127
7128         /**
7129          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7130          * the new height may not be available immediately.
7131          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7132          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7133          * @param {Function} onComplete (optional) Function to call when animation completes
7134          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7135          * @return {Roo.Element} this
7136          */
7137         autoHeight : function(animate, duration, onComplete, easing){
7138             var oldHeight = this.getHeight();
7139             this.clip();
7140             this.setHeight(1); // force clipping
7141             setTimeout(function(){
7142                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7143                 if(!animate){
7144                     this.setHeight(height);
7145                     this.unclip();
7146                     if(typeof onComplete == "function"){
7147                         onComplete();
7148                     }
7149                 }else{
7150                     this.setHeight(oldHeight); // restore original height
7151                     this.setHeight(height, animate, duration, function(){
7152                         this.unclip();
7153                         if(typeof onComplete == "function") onComplete();
7154                     }.createDelegate(this), easing);
7155                 }
7156             }.createDelegate(this), 0);
7157             return this;
7158         },
7159
7160         /**
7161          * Returns true if this element is an ancestor of the passed element
7162          * @param {HTMLElement/String} el The element to check
7163          * @return {Boolean} True if this element is an ancestor of el, else false
7164          */
7165         contains : function(el){
7166             if(!el){return false;}
7167             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7168         },
7169
7170         /**
7171          * Checks whether the element is currently visible using both visibility and display properties.
7172          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7173          * @return {Boolean} True if the element is currently visible, else false
7174          */
7175         isVisible : function(deep) {
7176             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7177             if(deep !== true || !vis){
7178                 return vis;
7179             }
7180             var p = this.dom.parentNode;
7181             while(p && p.tagName.toLowerCase() != "body"){
7182                 if(!Roo.fly(p, '_isVisible').isVisible()){
7183                     return false;
7184                 }
7185                 p = p.parentNode;
7186             }
7187             return true;
7188         },
7189
7190         /**
7191          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7192          * @param {String} selector The CSS selector
7193          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7194          * @return {CompositeElement/CompositeElementLite} The composite element
7195          */
7196         select : function(selector, unique){
7197             return El.select(selector, unique, this.dom);
7198         },
7199
7200         /**
7201          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7202          * @param {String} selector The CSS selector
7203          * @return {Array} An array of the matched nodes
7204          */
7205         query : function(selector, unique){
7206             return Roo.DomQuery.select(selector, this.dom);
7207         },
7208
7209         /**
7210          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7211          * @param {String} selector The CSS selector
7212          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7213          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7214          */
7215         child : function(selector, returnDom){
7216             var n = Roo.DomQuery.selectNode(selector, this.dom);
7217             return returnDom ? n : Roo.get(n);
7218         },
7219
7220         /**
7221          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7222          * @param {String} selector The CSS selector
7223          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7224          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7225          */
7226         down : function(selector, returnDom){
7227             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7228             return returnDom ? n : Roo.get(n);
7229         },
7230
7231         /**
7232          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7233          * @param {String} group The group the DD object is member of
7234          * @param {Object} config The DD config object
7235          * @param {Object} overrides An object containing methods to override/implement on the DD object
7236          * @return {Roo.dd.DD} The DD object
7237          */
7238         initDD : function(group, config, overrides){
7239             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7240             return Roo.apply(dd, overrides);
7241         },
7242
7243         /**
7244          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7245          * @param {String} group The group the DDProxy object is member of
7246          * @param {Object} config The DDProxy config object
7247          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7248          * @return {Roo.dd.DDProxy} The DDProxy object
7249          */
7250         initDDProxy : function(group, config, overrides){
7251             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7252             return Roo.apply(dd, overrides);
7253         },
7254
7255         /**
7256          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7257          * @param {String} group The group the DDTarget object is member of
7258          * @param {Object} config The DDTarget config object
7259          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7260          * @return {Roo.dd.DDTarget} The DDTarget object
7261          */
7262         initDDTarget : function(group, config, overrides){
7263             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7264             return Roo.apply(dd, overrides);
7265         },
7266
7267         /**
7268          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7269          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7270          * @param {Boolean} visible Whether the element is visible
7271          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7272          * @return {Roo.Element} this
7273          */
7274          setVisible : function(visible, animate){
7275             if(!animate || !A){
7276                 if(this.visibilityMode == El.DISPLAY){
7277                     this.setDisplayed(visible);
7278                 }else{
7279                     this.fixDisplay();
7280                     this.dom.style.visibility = visible ? "visible" : "hidden";
7281                 }
7282             }else{
7283                 // closure for composites
7284                 var dom = this.dom;
7285                 var visMode = this.visibilityMode;
7286                 if(visible){
7287                     this.setOpacity(.01);
7288                     this.setVisible(true);
7289                 }
7290                 this.anim({opacity: { to: (visible?1:0) }},
7291                       this.preanim(arguments, 1),
7292                       null, .35, 'easeIn', function(){
7293                          if(!visible){
7294                              if(visMode == El.DISPLAY){
7295                                  dom.style.display = "none";
7296                              }else{
7297                                  dom.style.visibility = "hidden";
7298                              }
7299                              Roo.get(dom).setOpacity(1);
7300                          }
7301                      });
7302             }
7303             return this;
7304         },
7305
7306         /**
7307          * Returns true if display is not "none"
7308          * @return {Boolean}
7309          */
7310         isDisplayed : function() {
7311             return this.getStyle("display") != "none";
7312         },
7313
7314         /**
7315          * Toggles the element's visibility or display, depending on visibility mode.
7316          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7317          * @return {Roo.Element} this
7318          */
7319         toggle : function(animate){
7320             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7321             return this;
7322         },
7323
7324         /**
7325          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7326          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7327          * @return {Roo.Element} this
7328          */
7329         setDisplayed : function(value) {
7330             if(typeof value == "boolean"){
7331                value = value ? this.originalDisplay : "none";
7332             }
7333             this.setStyle("display", value);
7334             return this;
7335         },
7336
7337         /**
7338          * Tries to focus the element. Any exceptions are caught and ignored.
7339          * @return {Roo.Element} this
7340          */
7341         focus : function() {
7342             try{
7343                 this.dom.focus();
7344             }catch(e){}
7345             return this;
7346         },
7347
7348         /**
7349          * Tries to blur the element. Any exceptions are caught and ignored.
7350          * @return {Roo.Element} this
7351          */
7352         blur : function() {
7353             try{
7354                 this.dom.blur();
7355             }catch(e){}
7356             return this;
7357         },
7358
7359         /**
7360          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7361          * @param {String/Array} className The CSS class to add, or an array of classes
7362          * @return {Roo.Element} this
7363          */
7364         addClass : function(className){
7365             if(className instanceof Array){
7366                 for(var i = 0, len = className.length; i < len; i++) {
7367                     this.addClass(className[i]);
7368                 }
7369             }else{
7370                 if(className && !this.hasClass(className)){
7371                     this.dom.className = this.dom.className + " " + className;
7372                 }
7373             }
7374             return this;
7375         },
7376
7377         /**
7378          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7379          * @param {String/Array} className The CSS class to add, or an array of classes
7380          * @return {Roo.Element} this
7381          */
7382         radioClass : function(className){
7383             var siblings = this.dom.parentNode.childNodes;
7384             for(var i = 0; i < siblings.length; i++) {
7385                 var s = siblings[i];
7386                 if(s.nodeType == 1){
7387                     Roo.get(s).removeClass(className);
7388                 }
7389             }
7390             this.addClass(className);
7391             return this;
7392         },
7393
7394         /**
7395          * Removes one or more CSS classes from the element.
7396          * @param {String/Array} className The CSS class to remove, or an array of classes
7397          * @return {Roo.Element} this
7398          */
7399         removeClass : function(className){
7400             if(!className || !this.dom.className){
7401                 return this;
7402             }
7403             if(className instanceof Array){
7404                 for(var i = 0, len = className.length; i < len; i++) {
7405                     this.removeClass(className[i]);
7406                 }
7407             }else{
7408                 if(this.hasClass(className)){
7409                     var re = this.classReCache[className];
7410                     if (!re) {
7411                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7412                        this.classReCache[className] = re;
7413                     }
7414                     this.dom.className =
7415                         this.dom.className.replace(re, " ");
7416                 }
7417             }
7418             return this;
7419         },
7420
7421         // private
7422         classReCache: {},
7423
7424         /**
7425          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7426          * @param {String} className The CSS class to toggle
7427          * @return {Roo.Element} this
7428          */
7429         toggleClass : function(className){
7430             if(this.hasClass(className)){
7431                 this.removeClass(className);
7432             }else{
7433                 this.addClass(className);
7434             }
7435             return this;
7436         },
7437
7438         /**
7439          * Checks if the specified CSS class exists on this element's DOM node.
7440          * @param {String} className The CSS class to check for
7441          * @return {Boolean} True if the class exists, else false
7442          */
7443         hasClass : function(className){
7444             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7445         },
7446
7447         /**
7448          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7449          * @param {String} oldClassName The CSS class to replace
7450          * @param {String} newClassName The replacement CSS class
7451          * @return {Roo.Element} this
7452          */
7453         replaceClass : function(oldClassName, newClassName){
7454             this.removeClass(oldClassName);
7455             this.addClass(newClassName);
7456             return this;
7457         },
7458
7459         /**
7460          * Returns an object with properties matching the styles requested.
7461          * For example, el.getStyles('color', 'font-size', 'width') might return
7462          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7463          * @param {String} style1 A style name
7464          * @param {String} style2 A style name
7465          * @param {String} etc.
7466          * @return {Object} The style object
7467          */
7468         getStyles : function(){
7469             var a = arguments, len = a.length, r = {};
7470             for(var i = 0; i < len; i++){
7471                 r[a[i]] = this.getStyle(a[i]);
7472             }
7473             return r;
7474         },
7475
7476         /**
7477          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7478          * @param {String} property The style property whose value is returned.
7479          * @return {String} The current value of the style property for this element.
7480          */
7481         getStyle : function(){
7482             return view && view.getComputedStyle ?
7483                 function(prop){
7484                     var el = this.dom, v, cs, camel;
7485                     if(prop == 'float'){
7486                         prop = "cssFloat";
7487                     }
7488                     if(el.style && (v = el.style[prop])){
7489                         return v;
7490                     }
7491                     if(cs = view.getComputedStyle(el, "")){
7492                         if(!(camel = propCache[prop])){
7493                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7494                         }
7495                         return cs[camel];
7496                     }
7497                     return null;
7498                 } :
7499                 function(prop){
7500                     var el = this.dom, v, cs, camel;
7501                     if(prop == 'opacity'){
7502                         if(typeof el.style.filter == 'string'){
7503                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7504                             if(m){
7505                                 var fv = parseFloat(m[1]);
7506                                 if(!isNaN(fv)){
7507                                     return fv ? fv / 100 : 0;
7508                                 }
7509                             }
7510                         }
7511                         return 1;
7512                     }else if(prop == 'float'){
7513                         prop = "styleFloat";
7514                     }
7515                     if(!(camel = propCache[prop])){
7516                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7517                     }
7518                     if(v = el.style[camel]){
7519                         return v;
7520                     }
7521                     if(cs = el.currentStyle){
7522                         return cs[camel];
7523                     }
7524                     return null;
7525                 };
7526         }(),
7527
7528         /**
7529          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7530          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7531          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7532          * @return {Roo.Element} this
7533          */
7534         setStyle : function(prop, value){
7535             if(typeof prop == "string"){
7536                 
7537                 if (prop == 'float') {
7538                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7539                     return this;
7540                 }
7541                 
7542                 var camel;
7543                 if(!(camel = propCache[prop])){
7544                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7545                 }
7546                 
7547                 if(camel == 'opacity') {
7548                     this.setOpacity(value);
7549                 }else{
7550                     this.dom.style[camel] = value;
7551                 }
7552             }else{
7553                 for(var style in prop){
7554                     if(typeof prop[style] != "function"){
7555                        this.setStyle(style, prop[style]);
7556                     }
7557                 }
7558             }
7559             return this;
7560         },
7561
7562         /**
7563          * More flexible version of {@link #setStyle} for setting style properties.
7564          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7565          * a function which returns such a specification.
7566          * @return {Roo.Element} this
7567          */
7568         applyStyles : function(style){
7569             Roo.DomHelper.applyStyles(this.dom, style);
7570             return this;
7571         },
7572
7573         /**
7574           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7575           * @return {Number} The X position of the element
7576           */
7577         getX : function(){
7578             return D.getX(this.dom);
7579         },
7580
7581         /**
7582           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7583           * @return {Number} The Y position of the element
7584           */
7585         getY : function(){
7586             return D.getY(this.dom);
7587         },
7588
7589         /**
7590           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7591           * @return {Array} The XY position of the element
7592           */
7593         getXY : function(){
7594             return D.getXY(this.dom);
7595         },
7596
7597         /**
7598          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7599          * @param {Number} The X position of the element
7600          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7601          * @return {Roo.Element} this
7602          */
7603         setX : function(x, animate){
7604             if(!animate || !A){
7605                 D.setX(this.dom, x);
7606             }else{
7607                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7608             }
7609             return this;
7610         },
7611
7612         /**
7613          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7614          * @param {Number} The Y position of the element
7615          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7616          * @return {Roo.Element} this
7617          */
7618         setY : function(y, animate){
7619             if(!animate || !A){
7620                 D.setY(this.dom, y);
7621             }else{
7622                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7623             }
7624             return this;
7625         },
7626
7627         /**
7628          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7629          * @param {String} left The left CSS property value
7630          * @return {Roo.Element} this
7631          */
7632         setLeft : function(left){
7633             this.setStyle("left", this.addUnits(left));
7634             return this;
7635         },
7636
7637         /**
7638          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7639          * @param {String} top The top CSS property value
7640          * @return {Roo.Element} this
7641          */
7642         setTop : function(top){
7643             this.setStyle("top", this.addUnits(top));
7644             return this;
7645         },
7646
7647         /**
7648          * Sets the element's CSS right style.
7649          * @param {String} right The right CSS property value
7650          * @return {Roo.Element} this
7651          */
7652         setRight : function(right){
7653             this.setStyle("right", this.addUnits(right));
7654             return this;
7655         },
7656
7657         /**
7658          * Sets the element's CSS bottom style.
7659          * @param {String} bottom The bottom CSS property value
7660          * @return {Roo.Element} this
7661          */
7662         setBottom : function(bottom){
7663             this.setStyle("bottom", this.addUnits(bottom));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7669          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7670          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7671          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7672          * @return {Roo.Element} this
7673          */
7674         setXY : function(pos, animate){
7675             if(!animate || !A){
7676                 D.setXY(this.dom, pos);
7677             }else{
7678                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7679             }
7680             return this;
7681         },
7682
7683         /**
7684          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7685          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7686          * @param {Number} x X value for new position (coordinates are page-based)
7687          * @param {Number} y Y value for new position (coordinates are page-based)
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setLocation : function(x, y, animate){
7692             this.setXY([x, y], this.preanim(arguments, 2));
7693             return this;
7694         },
7695
7696         /**
7697          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7698          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699          * @param {Number} x X value for new position (coordinates are page-based)
7700          * @param {Number} y Y value for new position (coordinates are page-based)
7701          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7702          * @return {Roo.Element} this
7703          */
7704         moveTo : function(x, y, animate){
7705             this.setXY([x, y], this.preanim(arguments, 2));
7706             return this;
7707         },
7708
7709         /**
7710          * Returns the region of the given element.
7711          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7712          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7713          */
7714         getRegion : function(){
7715             return D.getRegion(this.dom);
7716         },
7717
7718         /**
7719          * Returns the offset height of the element
7720          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7721          * @return {Number} The element's height
7722          */
7723         getHeight : function(contentHeight){
7724             var h = this.dom.offsetHeight || 0;
7725             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7726         },
7727
7728         /**
7729          * Returns the offset width of the element
7730          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7731          * @return {Number} The element's width
7732          */
7733         getWidth : function(contentWidth){
7734             var w = this.dom.offsetWidth || 0;
7735             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7736         },
7737
7738         /**
7739          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7740          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7741          * if a height has not been set using CSS.
7742          * @return {Number}
7743          */
7744         getComputedHeight : function(){
7745             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7746             if(!h){
7747                 h = parseInt(this.getStyle('height'), 10) || 0;
7748                 if(!this.isBorderBox()){
7749                     h += this.getFrameWidth('tb');
7750                 }
7751             }
7752             return h;
7753         },
7754
7755         /**
7756          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7757          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7758          * if a width has not been set using CSS.
7759          * @return {Number}
7760          */
7761         getComputedWidth : function(){
7762             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7763             if(!w){
7764                 w = parseInt(this.getStyle('width'), 10) || 0;
7765                 if(!this.isBorderBox()){
7766                     w += this.getFrameWidth('lr');
7767                 }
7768             }
7769             return w;
7770         },
7771
7772         /**
7773          * Returns the size of the element.
7774          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7775          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7776          */
7777         getSize : function(contentSize){
7778             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7779         },
7780
7781         /**
7782          * Returns the width and height of the viewport.
7783          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7784          */
7785         getViewSize : function(){
7786             var d = this.dom, doc = document, aw = 0, ah = 0;
7787             if(d == doc || d == doc.body){
7788                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7789             }else{
7790                 return {
7791                     width : d.clientWidth,
7792                     height: d.clientHeight
7793                 };
7794             }
7795         },
7796
7797         /**
7798          * Returns the value of the "value" attribute
7799          * @param {Boolean} asNumber true to parse the value as a number
7800          * @return {String/Number}
7801          */
7802         getValue : function(asNumber){
7803             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7804         },
7805
7806         // private
7807         adjustWidth : function(width){
7808             if(typeof width == "number"){
7809                 if(this.autoBoxAdjust && !this.isBorderBox()){
7810                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7811                 }
7812                 if(width < 0){
7813                     width = 0;
7814                 }
7815             }
7816             return width;
7817         },
7818
7819         // private
7820         adjustHeight : function(height){
7821             if(typeof height == "number"){
7822                if(this.autoBoxAdjust && !this.isBorderBox()){
7823                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7824                }
7825                if(height < 0){
7826                    height = 0;
7827                }
7828             }
7829             return height;
7830         },
7831
7832         /**
7833          * Set the width of the element
7834          * @param {Number} width The new width
7835          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7836          * @return {Roo.Element} this
7837          */
7838         setWidth : function(width, animate){
7839             width = this.adjustWidth(width);
7840             if(!animate || !A){
7841                 this.dom.style.width = this.addUnits(width);
7842             }else{
7843                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7844             }
7845             return this;
7846         },
7847
7848         /**
7849          * Set the height of the element
7850          * @param {Number} height The new height
7851          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7852          * @return {Roo.Element} this
7853          */
7854          setHeight : function(height, animate){
7855             height = this.adjustHeight(height);
7856             if(!animate || !A){
7857                 this.dom.style.height = this.addUnits(height);
7858             }else{
7859                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7860             }
7861             return this;
7862         },
7863
7864         /**
7865          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7866          * @param {Number} width The new width
7867          * @param {Number} height The new height
7868          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7869          * @return {Roo.Element} this
7870          */
7871          setSize : function(width, height, animate){
7872             if(typeof width == "object"){ // in case of object from getSize()
7873                 height = width.height; width = width.width;
7874             }
7875             width = this.adjustWidth(width); height = this.adjustHeight(height);
7876             if(!animate || !A){
7877                 this.dom.style.width = this.addUnits(width);
7878                 this.dom.style.height = this.addUnits(height);
7879             }else{
7880                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7887          * @param {Number} x X value for new position (coordinates are page-based)
7888          * @param {Number} y Y value for new position (coordinates are page-based)
7889          * @param {Number} width The new width
7890          * @param {Number} height The new height
7891          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7892          * @return {Roo.Element} this
7893          */
7894         setBounds : function(x, y, width, height, animate){
7895             if(!animate || !A){
7896                 this.setSize(width, height);
7897                 this.setLocation(x, y);
7898             }else{
7899                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7900                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7901                               this.preanim(arguments, 4), 'motion');
7902             }
7903             return this;
7904         },
7905
7906         /**
7907          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7908          * @param {Roo.lib.Region} region The region to fill
7909          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7910          * @return {Roo.Element} this
7911          */
7912         setRegion : function(region, animate){
7913             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7914             return this;
7915         },
7916
7917         /**
7918          * Appends an event handler
7919          *
7920          * @param {String}   eventName     The type of event to append
7921          * @param {Function} fn        The method the event invokes
7922          * @param {Object} scope       (optional) The scope (this object) of the fn
7923          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7924          */
7925         addListener : function(eventName, fn, scope, options){
7926             if (this.dom) {
7927                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7928             }
7929         },
7930
7931         /**
7932          * Removes an event handler from this element
7933          * @param {String} eventName the type of event to remove
7934          * @param {Function} fn the method the event invokes
7935          * @return {Roo.Element} this
7936          */
7937         removeListener : function(eventName, fn){
7938             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7939             return this;
7940         },
7941
7942         /**
7943          * Removes all previous added listeners from this element
7944          * @return {Roo.Element} this
7945          */
7946         removeAllListeners : function(){
7947             E.purgeElement(this.dom);
7948             return this;
7949         },
7950
7951         relayEvent : function(eventName, observable){
7952             this.on(eventName, function(e){
7953                 observable.fireEvent(eventName, e);
7954             });
7955         },
7956
7957         /**
7958          * Set the opacity of the element
7959          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7960          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7961          * @return {Roo.Element} this
7962          */
7963          setOpacity : function(opacity, animate){
7964             if(!animate || !A){
7965                 var s = this.dom.style;
7966                 if(Roo.isIE){
7967                     s.zoom = 1;
7968                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7969                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7970                 }else{
7971                     s.opacity = opacity;
7972                 }
7973             }else{
7974                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * Gets the left X coordinate
7981          * @param {Boolean} local True to get the local css position instead of page coordinate
7982          * @return {Number}
7983          */
7984         getLeft : function(local){
7985             if(!local){
7986                 return this.getX();
7987             }else{
7988                 return parseInt(this.getStyle("left"), 10) || 0;
7989             }
7990         },
7991
7992         /**
7993          * Gets the right X coordinate of the element (element X position + element width)
7994          * @param {Boolean} local True to get the local css position instead of page coordinate
7995          * @return {Number}
7996          */
7997         getRight : function(local){
7998             if(!local){
7999                 return this.getX() + this.getWidth();
8000             }else{
8001                 return (this.getLeft(true) + this.getWidth()) || 0;
8002             }
8003         },
8004
8005         /**
8006          * Gets the top Y coordinate
8007          * @param {Boolean} local True to get the local css position instead of page coordinate
8008          * @return {Number}
8009          */
8010         getTop : function(local) {
8011             if(!local){
8012                 return this.getY();
8013             }else{
8014                 return parseInt(this.getStyle("top"), 10) || 0;
8015             }
8016         },
8017
8018         /**
8019          * Gets the bottom Y coordinate of the element (element Y position + element height)
8020          * @param {Boolean} local True to get the local css position instead of page coordinate
8021          * @return {Number}
8022          */
8023         getBottom : function(local){
8024             if(!local){
8025                 return this.getY() + this.getHeight();
8026             }else{
8027                 return (this.getTop(true) + this.getHeight()) || 0;
8028             }
8029         },
8030
8031         /**
8032         * Initializes positioning on this element. If a desired position is not passed, it will make the
8033         * the element positioned relative IF it is not already positioned.
8034         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8035         * @param {Number} zIndex (optional) The zIndex to apply
8036         * @param {Number} x (optional) Set the page X position
8037         * @param {Number} y (optional) Set the page Y position
8038         */
8039         position : function(pos, zIndex, x, y){
8040             if(!pos){
8041                if(this.getStyle('position') == 'static'){
8042                    this.setStyle('position', 'relative');
8043                }
8044             }else{
8045                 this.setStyle("position", pos);
8046             }
8047             if(zIndex){
8048                 this.setStyle("z-index", zIndex);
8049             }
8050             if(x !== undefined && y !== undefined){
8051                 this.setXY([x, y]);
8052             }else if(x !== undefined){
8053                 this.setX(x);
8054             }else if(y !== undefined){
8055                 this.setY(y);
8056             }
8057         },
8058
8059         /**
8060         * Clear positioning back to the default when the document was loaded
8061         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8062         * @return {Roo.Element} this
8063          */
8064         clearPositioning : function(value){
8065             value = value ||'';
8066             this.setStyle({
8067                 "left": value,
8068                 "right": value,
8069                 "top": value,
8070                 "bottom": value,
8071                 "z-index": "",
8072                 "position" : "static"
8073             });
8074             return this;
8075         },
8076
8077         /**
8078         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8079         * snapshot before performing an update and then restoring the element.
8080         * @return {Object}
8081         */
8082         getPositioning : function(){
8083             var l = this.getStyle("left");
8084             var t = this.getStyle("top");
8085             return {
8086                 "position" : this.getStyle("position"),
8087                 "left" : l,
8088                 "right" : l ? "" : this.getStyle("right"),
8089                 "top" : t,
8090                 "bottom" : t ? "" : this.getStyle("bottom"),
8091                 "z-index" : this.getStyle("z-index")
8092             };
8093         },
8094
8095         /**
8096          * Gets the width of the border(s) for the specified side(s)
8097          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8098          * passing lr would get the border (l)eft width + the border (r)ight width.
8099          * @return {Number} The width of the sides passed added together
8100          */
8101         getBorderWidth : function(side){
8102             return this.addStyles(side, El.borders);
8103         },
8104
8105         /**
8106          * Gets the width of the padding(s) for the specified side(s)
8107          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8108          * passing lr would get the padding (l)eft + the padding (r)ight.
8109          * @return {Number} The padding of the sides passed added together
8110          */
8111         getPadding : function(side){
8112             return this.addStyles(side, El.paddings);
8113         },
8114
8115         /**
8116         * Set positioning with an object returned by getPositioning().
8117         * @param {Object} posCfg
8118         * @return {Roo.Element} this
8119          */
8120         setPositioning : function(pc){
8121             this.applyStyles(pc);
8122             if(pc.right == "auto"){
8123                 this.dom.style.right = "";
8124             }
8125             if(pc.bottom == "auto"){
8126                 this.dom.style.bottom = "";
8127             }
8128             return this;
8129         },
8130
8131         // private
8132         fixDisplay : function(){
8133             if(this.getStyle("display") == "none"){
8134                 this.setStyle("visibility", "hidden");
8135                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8136                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8137                     this.setStyle("display", "block");
8138                 }
8139             }
8140         },
8141
8142         /**
8143          * Quick set left and top adding default units
8144          * @param {String} left The left CSS property value
8145          * @param {String} top The top CSS property value
8146          * @return {Roo.Element} this
8147          */
8148          setLeftTop : function(left, top){
8149             this.dom.style.left = this.addUnits(left);
8150             this.dom.style.top = this.addUnits(top);
8151             return this;
8152         },
8153
8154         /**
8155          * Move this element relative to its current position.
8156          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8157          * @param {Number} distance How far to move the element in pixels
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          move : function(direction, distance, animate){
8162             var xy = this.getXY();
8163             direction = direction.toLowerCase();
8164             switch(direction){
8165                 case "l":
8166                 case "left":
8167                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8168                     break;
8169                case "r":
8170                case "right":
8171                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8172                     break;
8173                case "t":
8174                case "top":
8175                case "up":
8176                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8177                     break;
8178                case "b":
8179                case "bottom":
8180                case "down":
8181                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8182                     break;
8183             }
8184             return this;
8185         },
8186
8187         /**
8188          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8189          * @return {Roo.Element} this
8190          */
8191         clip : function(){
8192             if(!this.isClipped){
8193                this.isClipped = true;
8194                this.originalClip = {
8195                    "o": this.getStyle("overflow"),
8196                    "x": this.getStyle("overflow-x"),
8197                    "y": this.getStyle("overflow-y")
8198                };
8199                this.setStyle("overflow", "hidden");
8200                this.setStyle("overflow-x", "hidden");
8201                this.setStyle("overflow-y", "hidden");
8202             }
8203             return this;
8204         },
8205
8206         /**
8207          *  Return clipping (overflow) to original clipping before clip() was called
8208          * @return {Roo.Element} this
8209          */
8210         unclip : function(){
8211             if(this.isClipped){
8212                 this.isClipped = false;
8213                 var o = this.originalClip;
8214                 if(o.o){this.setStyle("overflow", o.o);}
8215                 if(o.x){this.setStyle("overflow-x", o.x);}
8216                 if(o.y){this.setStyle("overflow-y", o.y);}
8217             }
8218             return this;
8219         },
8220
8221
8222         /**
8223          * Gets the x,y coordinates specified by the anchor position on the element.
8224          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8225          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8226          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8227          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8228          * @return {Array} [x, y] An array containing the element's x and y coordinates
8229          */
8230         getAnchorXY : function(anchor, local, s){
8231             //Passing a different size is useful for pre-calculating anchors,
8232             //especially for anchored animations that change the el size.
8233
8234             var w, h, vp = false;
8235             if(!s){
8236                 var d = this.dom;
8237                 if(d == document.body || d == document){
8238                     vp = true;
8239                     w = D.getViewWidth(); h = D.getViewHeight();
8240                 }else{
8241                     w = this.getWidth(); h = this.getHeight();
8242                 }
8243             }else{
8244                 w = s.width;  h = s.height;
8245             }
8246             var x = 0, y = 0, r = Math.round;
8247             switch((anchor || "tl").toLowerCase()){
8248                 case "c":
8249                     x = r(w*.5);
8250                     y = r(h*.5);
8251                 break;
8252                 case "t":
8253                     x = r(w*.5);
8254                     y = 0;
8255                 break;
8256                 case "l":
8257                     x = 0;
8258                     y = r(h*.5);
8259                 break;
8260                 case "r":
8261                     x = w;
8262                     y = r(h*.5);
8263                 break;
8264                 case "b":
8265                     x = r(w*.5);
8266                     y = h;
8267                 break;
8268                 case "tl":
8269                     x = 0;
8270                     y = 0;
8271                 break;
8272                 case "bl":
8273                     x = 0;
8274                     y = h;
8275                 break;
8276                 case "br":
8277                     x = w;
8278                     y = h;
8279                 break;
8280                 case "tr":
8281                     x = w;
8282                     y = 0;
8283                 break;
8284             }
8285             if(local === true){
8286                 return [x, y];
8287             }
8288             if(vp){
8289                 var sc = this.getScroll();
8290                 return [x + sc.left, y + sc.top];
8291             }
8292             //Add the element's offset xy
8293             var o = this.getXY();
8294             return [x+o[0], y+o[1]];
8295         },
8296
8297         /**
8298          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8299          * supported position values.
8300          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8301          * @param {String} position The position to align to.
8302          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8303          * @return {Array} [x, y]
8304          */
8305         getAlignToXY : function(el, p, o){
8306             el = Roo.get(el);
8307             var d = this.dom;
8308             if(!el.dom){
8309                 throw "Element.alignTo with an element that doesn't exist";
8310             }
8311             var c = false; //constrain to viewport
8312             var p1 = "", p2 = "";
8313             o = o || [0,0];
8314
8315             if(!p){
8316                 p = "tl-bl";
8317             }else if(p == "?"){
8318                 p = "tl-bl?";
8319             }else if(p.indexOf("-") == -1){
8320                 p = "tl-" + p;
8321             }
8322             p = p.toLowerCase();
8323             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8324             if(!m){
8325                throw "Element.alignTo with an invalid alignment " + p;
8326             }
8327             p1 = m[1]; p2 = m[2]; c = !!m[3];
8328
8329             //Subtract the aligned el's internal xy from the target's offset xy
8330             //plus custom offset to get the aligned el's new offset xy
8331             var a1 = this.getAnchorXY(p1, true);
8332             var a2 = el.getAnchorXY(p2, false);
8333             var x = a2[0] - a1[0] + o[0];
8334             var y = a2[1] - a1[1] + o[1];
8335             if(c){
8336                 //constrain the aligned el to viewport if necessary
8337                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8338                 // 5px of margin for ie
8339                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8340
8341                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8342                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8343                 //otherwise swap the aligned el to the opposite border of the target.
8344                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8345                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8346                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8347                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8348
8349                var doc = document;
8350                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8351                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8352
8353                if((x+w) > dw + scrollX){
8354                     x = swapX ? r.left-w : dw+scrollX-w;
8355                 }
8356                if(x < scrollX){
8357                    x = swapX ? r.right : scrollX;
8358                }
8359                if((y+h) > dh + scrollY){
8360                     y = swapY ? r.top-h : dh+scrollY-h;
8361                 }
8362                if (y < scrollY){
8363                    y = swapY ? r.bottom : scrollY;
8364                }
8365             }
8366             return [x,y];
8367         },
8368
8369         // private
8370         getConstrainToXY : function(){
8371             var os = {top:0, left:0, bottom:0, right: 0};
8372
8373             return function(el, local, offsets, proposedXY){
8374                 el = Roo.get(el);
8375                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8376
8377                 var vw, vh, vx = 0, vy = 0;
8378                 if(el.dom == document.body || el.dom == document){
8379                     vw = Roo.lib.Dom.getViewWidth();
8380                     vh = Roo.lib.Dom.getViewHeight();
8381                 }else{
8382                     vw = el.dom.clientWidth;
8383                     vh = el.dom.clientHeight;
8384                     if(!local){
8385                         var vxy = el.getXY();
8386                         vx = vxy[0];
8387                         vy = vxy[1];
8388                     }
8389                 }
8390
8391                 var s = el.getScroll();
8392
8393                 vx += offsets.left + s.left;
8394                 vy += offsets.top + s.top;
8395
8396                 vw -= offsets.right;
8397                 vh -= offsets.bottom;
8398
8399                 var vr = vx+vw;
8400                 var vb = vy+vh;
8401
8402                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8403                 var x = xy[0], y = xy[1];
8404                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8405
8406                 // only move it if it needs it
8407                 var moved = false;
8408
8409                 // first validate right/bottom
8410                 if((x + w) > vr){
8411                     x = vr - w;
8412                     moved = true;
8413                 }
8414                 if((y + h) > vb){
8415                     y = vb - h;
8416                     moved = true;
8417                 }
8418                 // then make sure top/left isn't negative
8419                 if(x < vx){
8420                     x = vx;
8421                     moved = true;
8422                 }
8423                 if(y < vy){
8424                     y = vy;
8425                     moved = true;
8426                 }
8427                 return moved ? [x, y] : false;
8428             };
8429         }(),
8430
8431         // private
8432         adjustForConstraints : function(xy, parent, offsets){
8433             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8434         },
8435
8436         /**
8437          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8438          * document it aligns it to the viewport.
8439          * The position parameter is optional, and can be specified in any one of the following formats:
8440          * <ul>
8441          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8442          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8443          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8444          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8445          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8446          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8447          * </ul>
8448          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8449          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8450          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8451          * that specified in order to enforce the viewport constraints.
8452          * Following are all of the supported anchor positions:
8453     <pre>
8454     Value  Description
8455     -----  -----------------------------
8456     tl     The top left corner (default)
8457     t      The center of the top edge
8458     tr     The top right corner
8459     l      The center of the left edge
8460     c      In the center of the element
8461     r      The center of the right edge
8462     bl     The bottom left corner
8463     b      The center of the bottom edge
8464     br     The bottom right corner
8465     </pre>
8466     Example Usage:
8467     <pre><code>
8468     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8469     el.alignTo("other-el");
8470
8471     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8472     el.alignTo("other-el", "tr?");
8473
8474     // align the bottom right corner of el with the center left edge of other-el
8475     el.alignTo("other-el", "br-l?");
8476
8477     // align the center of el with the bottom left corner of other-el and
8478     // adjust the x position by -6 pixels (and the y position by 0)
8479     el.alignTo("other-el", "c-bl", [-6, 0]);
8480     </code></pre>
8481          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8482          * @param {String} position The position to align to.
8483          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8484          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8485          * @return {Roo.Element} this
8486          */
8487         alignTo : function(element, position, offsets, animate){
8488             var xy = this.getAlignToXY(element, position, offsets);
8489             this.setXY(xy, this.preanim(arguments, 3));
8490             return this;
8491         },
8492
8493         /**
8494          * Anchors an element to another element and realigns it when the window is resized.
8495          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8496          * @param {String} position The position to align to.
8497          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8498          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8499          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8500          * is a number, it is used as the buffer delay (defaults to 50ms).
8501          * @param {Function} callback The function to call after the animation finishes
8502          * @return {Roo.Element} this
8503          */
8504         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8505             var action = function(){
8506                 this.alignTo(el, alignment, offsets, animate);
8507                 Roo.callback(callback, this);
8508             };
8509             Roo.EventManager.onWindowResize(action, this);
8510             var tm = typeof monitorScroll;
8511             if(tm != 'undefined'){
8512                 Roo.EventManager.on(window, 'scroll', action, this,
8513                     {buffer: tm == 'number' ? monitorScroll : 50});
8514             }
8515             action.call(this); // align immediately
8516             return this;
8517         },
8518         /**
8519          * Clears any opacity settings from this element. Required in some cases for IE.
8520          * @return {Roo.Element} this
8521          */
8522         clearOpacity : function(){
8523             if (window.ActiveXObject) {
8524                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8525                     this.dom.style.filter = "";
8526                 }
8527             } else {
8528                 this.dom.style.opacity = "";
8529                 this.dom.style["-moz-opacity"] = "";
8530                 this.dom.style["-khtml-opacity"] = "";
8531             }
8532             return this;
8533         },
8534
8535         /**
8536          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8537          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8538          * @return {Roo.Element} this
8539          */
8540         hide : function(animate){
8541             this.setVisible(false, this.preanim(arguments, 0));
8542             return this;
8543         },
8544
8545         /**
8546         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8547         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8548          * @return {Roo.Element} this
8549          */
8550         show : function(animate){
8551             this.setVisible(true, this.preanim(arguments, 0));
8552             return this;
8553         },
8554
8555         /**
8556          * @private Test if size has a unit, otherwise appends the default
8557          */
8558         addUnits : function(size){
8559             return Roo.Element.addUnits(size, this.defaultUnit);
8560         },
8561
8562         /**
8563          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8564          * @return {Roo.Element} this
8565          */
8566         beginMeasure : function(){
8567             var el = this.dom;
8568             if(el.offsetWidth || el.offsetHeight){
8569                 return this; // offsets work already
8570             }
8571             var changed = [];
8572             var p = this.dom, b = document.body; // start with this element
8573             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8574                 var pe = Roo.get(p);
8575                 if(pe.getStyle('display') == 'none'){
8576                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8577                     p.style.visibility = "hidden";
8578                     p.style.display = "block";
8579                 }
8580                 p = p.parentNode;
8581             }
8582             this._measureChanged = changed;
8583             return this;
8584
8585         },
8586
8587         /**
8588          * Restores displays to before beginMeasure was called
8589          * @return {Roo.Element} this
8590          */
8591         endMeasure : function(){
8592             var changed = this._measureChanged;
8593             if(changed){
8594                 for(var i = 0, len = changed.length; i < len; i++) {
8595                     var r = changed[i];
8596                     r.el.style.visibility = r.visibility;
8597                     r.el.style.display = "none";
8598                 }
8599                 this._measureChanged = null;
8600             }
8601             return this;
8602         },
8603
8604         /**
8605         * Update the innerHTML of this element, optionally searching for and processing scripts
8606         * @param {String} html The new HTML
8607         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8608         * @param {Function} callback For async script loading you can be noticed when the update completes
8609         * @return {Roo.Element} this
8610          */
8611         update : function(html, loadScripts, callback){
8612             if(typeof html == "undefined"){
8613                 html = "";
8614             }
8615             if(loadScripts !== true){
8616                 this.dom.innerHTML = html;
8617                 if(typeof callback == "function"){
8618                     callback();
8619                 }
8620                 return this;
8621             }
8622             var id = Roo.id();
8623             var dom = this.dom;
8624
8625             html += '<span id="' + id + '"></span>';
8626
8627             E.onAvailable(id, function(){
8628                 var hd = document.getElementsByTagName("head")[0];
8629                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8630                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8631                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8632
8633                 var match;
8634                 while(match = re.exec(html)){
8635                     var attrs = match[1];
8636                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8637                     if(srcMatch && srcMatch[2]){
8638                        var s = document.createElement("script");
8639                        s.src = srcMatch[2];
8640                        var typeMatch = attrs.match(typeRe);
8641                        if(typeMatch && typeMatch[2]){
8642                            s.type = typeMatch[2];
8643                        }
8644                        hd.appendChild(s);
8645                     }else if(match[2] && match[2].length > 0){
8646                         if(window.execScript) {
8647                            window.execScript(match[2]);
8648                         } else {
8649                             /**
8650                              * eval:var:id
8651                              * eval:var:dom
8652                              * eval:var:html
8653                              * 
8654                              */
8655                            window.eval(match[2]);
8656                         }
8657                     }
8658                 }
8659                 var el = document.getElementById(id);
8660                 if(el){el.parentNode.removeChild(el);}
8661                 if(typeof callback == "function"){
8662                     callback();
8663                 }
8664             });
8665             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8666             return this;
8667         },
8668
8669         /**
8670          * Direct access to the UpdateManager update() method (takes the same parameters).
8671          * @param {String/Function} url The url for this request or a function to call to get the url
8672          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8673          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8674          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8675          * @return {Roo.Element} this
8676          */
8677         load : function(){
8678             var um = this.getUpdateManager();
8679             um.update.apply(um, arguments);
8680             return this;
8681         },
8682
8683         /**
8684         * Gets this element's UpdateManager
8685         * @return {Roo.UpdateManager} The UpdateManager
8686         */
8687         getUpdateManager : function(){
8688             if(!this.updateManager){
8689                 this.updateManager = new Roo.UpdateManager(this);
8690             }
8691             return this.updateManager;
8692         },
8693
8694         /**
8695          * Disables text selection for this element (normalized across browsers)
8696          * @return {Roo.Element} this
8697          */
8698         unselectable : function(){
8699             this.dom.unselectable = "on";
8700             this.swallowEvent("selectstart", true);
8701             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8702             this.addClass("x-unselectable");
8703             return this;
8704         },
8705
8706         /**
8707         * Calculates the x, y to center this element on the screen
8708         * @return {Array} The x, y values [x, y]
8709         */
8710         getCenterXY : function(){
8711             return this.getAlignToXY(document, 'c-c');
8712         },
8713
8714         /**
8715         * Centers the Element in either the viewport, or another Element.
8716         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8717         */
8718         center : function(centerIn){
8719             this.alignTo(centerIn || document, 'c-c');
8720             return this;
8721         },
8722
8723         /**
8724          * Tests various css rules/browsers to determine if this element uses a border box
8725          * @return {Boolean}
8726          */
8727         isBorderBox : function(){
8728             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8729         },
8730
8731         /**
8732          * Return a box {x, y, width, height} that can be used to set another elements
8733          * size/location to match this element.
8734          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8735          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8736          * @return {Object} box An object in the format {x, y, width, height}
8737          */
8738         getBox : function(contentBox, local){
8739             var xy;
8740             if(!local){
8741                 xy = this.getXY();
8742             }else{
8743                 var left = parseInt(this.getStyle("left"), 10) || 0;
8744                 var top = parseInt(this.getStyle("top"), 10) || 0;
8745                 xy = [left, top];
8746             }
8747             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8748             if(!contentBox){
8749                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8750             }else{
8751                 var l = this.getBorderWidth("l")+this.getPadding("l");
8752                 var r = this.getBorderWidth("r")+this.getPadding("r");
8753                 var t = this.getBorderWidth("t")+this.getPadding("t");
8754                 var b = this.getBorderWidth("b")+this.getPadding("b");
8755                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8756             }
8757             bx.right = bx.x + bx.width;
8758             bx.bottom = bx.y + bx.height;
8759             return bx;
8760         },
8761
8762         /**
8763          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8764          for more information about the sides.
8765          * @param {String} sides
8766          * @return {Number}
8767          */
8768         getFrameWidth : function(sides, onlyContentBox){
8769             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8770         },
8771
8772         /**
8773          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8774          * @param {Object} box The box to fill {x, y, width, height}
8775          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8776          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8777          * @return {Roo.Element} this
8778          */
8779         setBox : function(box, adjust, animate){
8780             var w = box.width, h = box.height;
8781             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8782                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8783                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8784             }
8785             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8786             return this;
8787         },
8788
8789         /**
8790          * Forces the browser to repaint this element
8791          * @return {Roo.Element} this
8792          */
8793          repaint : function(){
8794             var dom = this.dom;
8795             this.addClass("x-repaint");
8796             setTimeout(function(){
8797                 Roo.get(dom).removeClass("x-repaint");
8798             }, 1);
8799             return this;
8800         },
8801
8802         /**
8803          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8804          * then it returns the calculated width of the sides (see getPadding)
8805          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8806          * @return {Object/Number}
8807          */
8808         getMargins : function(side){
8809             if(!side){
8810                 return {
8811                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8812                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8813                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8814                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8815                 };
8816             }else{
8817                 return this.addStyles(side, El.margins);
8818              }
8819         },
8820
8821         // private
8822         addStyles : function(sides, styles){
8823             var val = 0, v, w;
8824             for(var i = 0, len = sides.length; i < len; i++){
8825                 v = this.getStyle(styles[sides.charAt(i)]);
8826                 if(v){
8827                      w = parseInt(v, 10);
8828                      if(w){ val += w; }
8829                 }
8830             }
8831             return val;
8832         },
8833
8834         /**
8835          * Creates a proxy element of this element
8836          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8837          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8838          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8839          * @return {Roo.Element} The new proxy element
8840          */
8841         createProxy : function(config, renderTo, matchBox){
8842             if(renderTo){
8843                 renderTo = Roo.getDom(renderTo);
8844             }else{
8845                 renderTo = document.body;
8846             }
8847             config = typeof config == "object" ?
8848                 config : {tag : "div", cls: config};
8849             var proxy = Roo.DomHelper.append(renderTo, config, true);
8850             if(matchBox){
8851                proxy.setBox(this.getBox());
8852             }
8853             return proxy;
8854         },
8855
8856         /**
8857          * Puts a mask over this element to disable user interaction. Requires core.css.
8858          * This method can only be applied to elements which accept child nodes.
8859          * @param {String} msg (optional) A message to display in the mask
8860          * @param {String} msgCls (optional) A css class to apply to the msg element
8861          * @return {Element} The mask  element
8862          */
8863         mask : function(msg, msgCls){
8864             if(this.getStyle("position") == "static"){
8865                 this.setStyle("position", "relative");
8866             }
8867             if(!this._mask){
8868                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8869             }
8870             this.addClass("x-masked");
8871             this._mask.setDisplayed(true);
8872             if(typeof msg == 'string'){
8873                 if(!this._maskMsg){
8874                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8875                 }
8876                 var mm = this._maskMsg;
8877                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8878                 mm.dom.firstChild.innerHTML = msg;
8879                 mm.setDisplayed(true);
8880                 mm.center(this);
8881             }
8882             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8883                 this._mask.setHeight(this.getHeight());
8884             }
8885             return this._mask;
8886         },
8887
8888         /**
8889          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8890          * it is cached for reuse.
8891          */
8892         unmask : function(removeEl){
8893             if(this._mask){
8894                 if(removeEl === true){
8895                     this._mask.remove();
8896                     delete this._mask;
8897                     if(this._maskMsg){
8898                         this._maskMsg.remove();
8899                         delete this._maskMsg;
8900                     }
8901                 }else{
8902                     this._mask.setDisplayed(false);
8903                     if(this._maskMsg){
8904                         this._maskMsg.setDisplayed(false);
8905                     }
8906                 }
8907             }
8908             this.removeClass("x-masked");
8909         },
8910
8911         /**
8912          * Returns true if this element is masked
8913          * @return {Boolean}
8914          */
8915         isMasked : function(){
8916             return this._mask && this._mask.isVisible();
8917         },
8918
8919         /**
8920          * Creates an iframe shim for this element to keep selects and other windowed objects from
8921          * showing through.
8922          * @return {Roo.Element} The new shim element
8923          */
8924         createShim : function(){
8925             var el = document.createElement('iframe');
8926             el.frameBorder = 'no';
8927             el.className = 'roo-shim';
8928             if(Roo.isIE && Roo.isSecure){
8929                 el.src = Roo.SSL_SECURE_URL;
8930             }
8931             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8932             shim.autoBoxAdjust = false;
8933             return shim;
8934         },
8935
8936         /**
8937          * Removes this element from the DOM and deletes it from the cache
8938          */
8939         remove : function(){
8940             if(this.dom.parentNode){
8941                 this.dom.parentNode.removeChild(this.dom);
8942             }
8943             delete El.cache[this.dom.id];
8944         },
8945
8946         /**
8947          * Sets up event handlers to add and remove a css class when the mouse is over this element
8948          * @param {String} className
8949          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8950          * mouseout events for children elements
8951          * @return {Roo.Element} this
8952          */
8953         addClassOnOver : function(className, preventFlicker){
8954             this.on("mouseover", function(){
8955                 Roo.fly(this, '_internal').addClass(className);
8956             }, this.dom);
8957             var removeFn = function(e){
8958                 if(preventFlicker !== true || !e.within(this, true)){
8959                     Roo.fly(this, '_internal').removeClass(className);
8960                 }
8961             };
8962             this.on("mouseout", removeFn, this.dom);
8963             return this;
8964         },
8965
8966         /**
8967          * Sets up event handlers to add and remove a css class when this element has the focus
8968          * @param {String} className
8969          * @return {Roo.Element} this
8970          */
8971         addClassOnFocus : function(className){
8972             this.on("focus", function(){
8973                 Roo.fly(this, '_internal').addClass(className);
8974             }, this.dom);
8975             this.on("blur", function(){
8976                 Roo.fly(this, '_internal').removeClass(className);
8977             }, this.dom);
8978             return this;
8979         },
8980         /**
8981          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8982          * @param {String} className
8983          * @return {Roo.Element} this
8984          */
8985         addClassOnClick : function(className){
8986             var dom = this.dom;
8987             this.on("mousedown", function(){
8988                 Roo.fly(dom, '_internal').addClass(className);
8989                 var d = Roo.get(document);
8990                 var fn = function(){
8991                     Roo.fly(dom, '_internal').removeClass(className);
8992                     d.removeListener("mouseup", fn);
8993                 };
8994                 d.on("mouseup", fn);
8995             });
8996             return this;
8997         },
8998
8999         /**
9000          * Stops the specified event from bubbling and optionally prevents the default action
9001          * @param {String} eventName
9002          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9003          * @return {Roo.Element} this
9004          */
9005         swallowEvent : function(eventName, preventDefault){
9006             var fn = function(e){
9007                 e.stopPropagation();
9008                 if(preventDefault){
9009                     e.preventDefault();
9010                 }
9011             };
9012             if(eventName instanceof Array){
9013                 for(var i = 0, len = eventName.length; i < len; i++){
9014                      this.on(eventName[i], fn);
9015                 }
9016                 return this;
9017             }
9018             this.on(eventName, fn);
9019             return this;
9020         },
9021
9022         /**
9023          * @private
9024          */
9025       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9026
9027         /**
9028          * Sizes this element to its parent element's dimensions performing
9029          * neccessary box adjustments.
9030          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9031          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9032          * @return {Roo.Element} this
9033          */
9034         fitToParent : function(monitorResize, targetParent) {
9035           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9036           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9037           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9038             return;
9039           }
9040           var p = Roo.get(targetParent || this.dom.parentNode);
9041           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9042           if (monitorResize === true) {
9043             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9044             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9045           }
9046           return this;
9047         },
9048
9049         /**
9050          * Gets the next sibling, skipping text nodes
9051          * @return {HTMLElement} The next sibling or null
9052          */
9053         getNextSibling : function(){
9054             var n = this.dom.nextSibling;
9055             while(n && n.nodeType != 1){
9056                 n = n.nextSibling;
9057             }
9058             return n;
9059         },
9060
9061         /**
9062          * Gets the previous sibling, skipping text nodes
9063          * @return {HTMLElement} The previous sibling or null
9064          */
9065         getPrevSibling : function(){
9066             var n = this.dom.previousSibling;
9067             while(n && n.nodeType != 1){
9068                 n = n.previousSibling;
9069             }
9070             return n;
9071         },
9072
9073
9074         /**
9075          * Appends the passed element(s) to this element
9076          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9077          * @return {Roo.Element} this
9078          */
9079         appendChild: function(el){
9080             el = Roo.get(el);
9081             el.appendTo(this);
9082             return this;
9083         },
9084
9085         /**
9086          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9087          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9088          * automatically generated with the specified attributes.
9089          * @param {HTMLElement} insertBefore (optional) a child element of this element
9090          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9091          * @return {Roo.Element} The new child element
9092          */
9093         createChild: function(config, insertBefore, returnDom){
9094             config = config || {tag:'div'};
9095             if(insertBefore){
9096                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9097             }
9098             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9099         },
9100
9101         /**
9102          * Appends this element to the passed element
9103          * @param {String/HTMLElement/Element} el The new parent element
9104          * @return {Roo.Element} this
9105          */
9106         appendTo: function(el){
9107             el = Roo.getDom(el);
9108             el.appendChild(this.dom);
9109             return this;
9110         },
9111
9112         /**
9113          * Inserts this element before the passed element in the DOM
9114          * @param {String/HTMLElement/Element} el The element to insert before
9115          * @return {Roo.Element} this
9116          */
9117         insertBefore: function(el){
9118             el = Roo.getDom(el);
9119             el.parentNode.insertBefore(this.dom, el);
9120             return this;
9121         },
9122
9123         /**
9124          * Inserts this element after the passed element in the DOM
9125          * @param {String/HTMLElement/Element} el The element to insert after
9126          * @return {Roo.Element} this
9127          */
9128         insertAfter: function(el){
9129             el = Roo.getDom(el);
9130             el.parentNode.insertBefore(this.dom, el.nextSibling);
9131             return this;
9132         },
9133
9134         /**
9135          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9136          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9137          * @return {Roo.Element} The new child
9138          */
9139         insertFirst: function(el, returnDom){
9140             el = el || {};
9141             if(typeof el == 'object' && !el.nodeType){ // dh config
9142                 return this.createChild(el, this.dom.firstChild, returnDom);
9143             }else{
9144                 el = Roo.getDom(el);
9145                 this.dom.insertBefore(el, this.dom.firstChild);
9146                 return !returnDom ? Roo.get(el) : el;
9147             }
9148         },
9149
9150         /**
9151          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9152          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9153          * @param {String} where (optional) 'before' or 'after' defaults to before
9154          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9155          * @return {Roo.Element} the inserted Element
9156          */
9157         insertSibling: function(el, where, returnDom){
9158             where = where ? where.toLowerCase() : 'before';
9159             el = el || {};
9160             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9161
9162             if(typeof el == 'object' && !el.nodeType){ // dh config
9163                 if(where == 'after' && !this.dom.nextSibling){
9164                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9165                 }else{
9166                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9167                 }
9168
9169             }else{
9170                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9171                             where == 'before' ? this.dom : this.dom.nextSibling);
9172                 if(!returnDom){
9173                     rt = Roo.get(rt);
9174                 }
9175             }
9176             return rt;
9177         },
9178
9179         /**
9180          * Creates and wraps this element with another element
9181          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9182          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9183          * @return {HTMLElement/Element} The newly created wrapper element
9184          */
9185         wrap: function(config, returnDom){
9186             if(!config){
9187                 config = {tag: "div"};
9188             }
9189             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9190             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9191             return newEl;
9192         },
9193
9194         /**
9195          * Replaces the passed element with this element
9196          * @param {String/HTMLElement/Element} el The element to replace
9197          * @return {Roo.Element} this
9198          */
9199         replace: function(el){
9200             el = Roo.get(el);
9201             this.insertBefore(el);
9202             el.remove();
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts an html fragment into this element
9208          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9209          * @param {String} html The HTML fragment
9210          * @param {Boolean} returnEl True to return an Roo.Element
9211          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9212          */
9213         insertHtml : function(where, html, returnEl){
9214             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9215             return returnEl ? Roo.get(el) : el;
9216         },
9217
9218         /**
9219          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9220          * @param {Object} o The object with the attributes
9221          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9222          * @return {Roo.Element} this
9223          */
9224         set : function(o, useSet){
9225             var el = this.dom;
9226             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9227             for(var attr in o){
9228                 if(attr == "style" || typeof o[attr] == "function") continue;
9229                 if(attr=="cls"){
9230                     el.className = o["cls"];
9231                 }else{
9232                     if(useSet) el.setAttribute(attr, o[attr]);
9233                     else el[attr] = o[attr];
9234                 }
9235             }
9236             if(o.style){
9237                 Roo.DomHelper.applyStyles(el, o.style);
9238             }
9239             return this;
9240         },
9241
9242         /**
9243          * Convenience method for constructing a KeyMap
9244          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9245          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9246          * @param {Function} fn The function to call
9247          * @param {Object} scope (optional) The scope of the function
9248          * @return {Roo.KeyMap} The KeyMap created
9249          */
9250         addKeyListener : function(key, fn, scope){
9251             var config;
9252             if(typeof key != "object" || key instanceof Array){
9253                 config = {
9254                     key: key,
9255                     fn: fn,
9256                     scope: scope
9257                 };
9258             }else{
9259                 config = {
9260                     key : key.key,
9261                     shift : key.shift,
9262                     ctrl : key.ctrl,
9263                     alt : key.alt,
9264                     fn: fn,
9265                     scope: scope
9266                 };
9267             }
9268             return new Roo.KeyMap(this, config);
9269         },
9270
9271         /**
9272          * Creates a KeyMap for this element
9273          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9274          * @return {Roo.KeyMap} The KeyMap created
9275          */
9276         addKeyMap : function(config){
9277             return new Roo.KeyMap(this, config);
9278         },
9279
9280         /**
9281          * Returns true if this element is scrollable.
9282          * @return {Boolean}
9283          */
9284          isScrollable : function(){
9285             var dom = this.dom;
9286             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9287         },
9288
9289         /**
9290          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9291          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9292          * @param {Number} value The new scroll value
9293          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9294          * @return {Element} this
9295          */
9296
9297         scrollTo : function(side, value, animate){
9298             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9299             if(!animate || !A){
9300                 this.dom[prop] = value;
9301             }else{
9302                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9303                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9304             }
9305             return this;
9306         },
9307
9308         /**
9309          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9310          * within this element's scrollable range.
9311          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9312          * @param {Number} distance How far to scroll the element in pixels
9313          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9314          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9315          * was scrolled as far as it could go.
9316          */
9317          scroll : function(direction, distance, animate){
9318              if(!this.isScrollable()){
9319                  return;
9320              }
9321              var el = this.dom;
9322              var l = el.scrollLeft, t = el.scrollTop;
9323              var w = el.scrollWidth, h = el.scrollHeight;
9324              var cw = el.clientWidth, ch = el.clientHeight;
9325              direction = direction.toLowerCase();
9326              var scrolled = false;
9327              var a = this.preanim(arguments, 2);
9328              switch(direction){
9329                  case "l":
9330                  case "left":
9331                      if(w - l > cw){
9332                          var v = Math.min(l + distance, w-cw);
9333                          this.scrollTo("left", v, a);
9334                          scrolled = true;
9335                      }
9336                      break;
9337                 case "r":
9338                 case "right":
9339                      if(l > 0){
9340                          var v = Math.max(l - distance, 0);
9341                          this.scrollTo("left", v, a);
9342                          scrolled = true;
9343                      }
9344                      break;
9345                 case "t":
9346                 case "top":
9347                 case "up":
9348                      if(t > 0){
9349                          var v = Math.max(t - distance, 0);
9350                          this.scrollTo("top", v, a);
9351                          scrolled = true;
9352                      }
9353                      break;
9354                 case "b":
9355                 case "bottom":
9356                 case "down":
9357                      if(h - t > ch){
9358                          var v = Math.min(t + distance, h-ch);
9359                          this.scrollTo("top", v, a);
9360                          scrolled = true;
9361                      }
9362                      break;
9363              }
9364              return scrolled;
9365         },
9366
9367         /**
9368          * Translates the passed page coordinates into left/top css values for this element
9369          * @param {Number/Array} x The page x or an array containing [x, y]
9370          * @param {Number} y The page y
9371          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9372          */
9373         translatePoints : function(x, y){
9374             if(typeof x == 'object' || x instanceof Array){
9375                 y = x[1]; x = x[0];
9376             }
9377             var p = this.getStyle('position');
9378             var o = this.getXY();
9379
9380             var l = parseInt(this.getStyle('left'), 10);
9381             var t = parseInt(this.getStyle('top'), 10);
9382
9383             if(isNaN(l)){
9384                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9385             }
9386             if(isNaN(t)){
9387                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9388             }
9389
9390             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9391         },
9392
9393         /**
9394          * Returns the current scroll position of the element.
9395          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9396          */
9397         getScroll : function(){
9398             var d = this.dom, doc = document;
9399             if(d == doc || d == doc.body){
9400                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9401                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9402                 return {left: l, top: t};
9403             }else{
9404                 return {left: d.scrollLeft, top: d.scrollTop};
9405             }
9406         },
9407
9408         /**
9409          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9410          * are convert to standard 6 digit hex color.
9411          * @param {String} attr The css attribute
9412          * @param {String} defaultValue The default value to use when a valid color isn't found
9413          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9414          * YUI color anims.
9415          */
9416         getColor : function(attr, defaultValue, prefix){
9417             var v = this.getStyle(attr);
9418             if(!v || v == "transparent" || v == "inherit") {
9419                 return defaultValue;
9420             }
9421             var color = typeof prefix == "undefined" ? "#" : prefix;
9422             if(v.substr(0, 4) == "rgb("){
9423                 var rvs = v.slice(4, v.length -1).split(",");
9424                 for(var i = 0; i < 3; i++){
9425                     var h = parseInt(rvs[i]).toString(16);
9426                     if(h < 16){
9427                         h = "0" + h;
9428                     }
9429                     color += h;
9430                 }
9431             } else {
9432                 if(v.substr(0, 1) == "#"){
9433                     if(v.length == 4) {
9434                         for(var i = 1; i < 4; i++){
9435                             var c = v.charAt(i);
9436                             color +=  c + c;
9437                         }
9438                     }else if(v.length == 7){
9439                         color += v.substr(1);
9440                     }
9441                 }
9442             }
9443             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9444         },
9445
9446         /**
9447          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9448          * gradient background, rounded corners and a 4-way shadow.
9449          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9450          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9451          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9452          * @return {Roo.Element} this
9453          */
9454         boxWrap : function(cls){
9455             cls = cls || 'x-box';
9456             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9457             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9458             return el;
9459         },
9460
9461         /**
9462          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9463          * @param {String} namespace The namespace in which to look for the attribute
9464          * @param {String} name The attribute name
9465          * @return {String} The attribute value
9466          */
9467         getAttributeNS : Roo.isIE ? function(ns, name){
9468             var d = this.dom;
9469             var type = typeof d[ns+":"+name];
9470             if(type != 'undefined' && type != 'unknown'){
9471                 return d[ns+":"+name];
9472             }
9473             return d[name];
9474         } : function(ns, name){
9475             var d = this.dom;
9476             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9477         }
9478     };
9479
9480     var ep = El.prototype;
9481
9482     /**
9483      * Appends an event handler (Shorthand for addListener)
9484      * @param {String}   eventName     The type of event to append
9485      * @param {Function} fn        The method the event invokes
9486      * @param {Object} scope       (optional) The scope (this object) of the fn
9487      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9488      * @method
9489      */
9490     ep.on = ep.addListener;
9491         // backwards compat
9492     ep.mon = ep.addListener;
9493
9494     /**
9495      * Removes an event handler from this element (shorthand for removeListener)
9496      * @param {String} eventName the type of event to remove
9497      * @param {Function} fn the method the event invokes
9498      * @return {Roo.Element} this
9499      * @method
9500      */
9501     ep.un = ep.removeListener;
9502
9503     /**
9504      * true to automatically adjust width and height settings for box-model issues (default to true)
9505      */
9506     ep.autoBoxAdjust = true;
9507
9508     // private
9509     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9510
9511     // private
9512     El.addUnits = function(v, defaultUnit){
9513         if(v === "" || v == "auto"){
9514             return v;
9515         }
9516         if(v === undefined){
9517             return '';
9518         }
9519         if(typeof v == "number" || !El.unitPattern.test(v)){
9520             return v + (defaultUnit || 'px');
9521         }
9522         return v;
9523     };
9524
9525     // special markup used throughout Roo when box wrapping elements
9526     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9527     /**
9528      * Visibility mode constant - Use visibility to hide element
9529      * @static
9530      * @type Number
9531      */
9532     El.VISIBILITY = 1;
9533     /**
9534      * Visibility mode constant - Use display to hide element
9535      * @static
9536      * @type Number
9537      */
9538     El.DISPLAY = 2;
9539
9540     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9541     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9542     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9543
9544
9545
9546     /**
9547      * @private
9548      */
9549     El.cache = {};
9550
9551     var docEl;
9552
9553     /**
9554      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9555      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9556      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9557      * @return {Element} The Element object
9558      * @static
9559      */
9560     El.get = function(el){
9561         var ex, elm, id;
9562         if(!el){ return null; }
9563         if(typeof el == "string"){ // element id
9564             if(!(elm = document.getElementById(el))){
9565                 return null;
9566             }
9567             if(ex = El.cache[el]){
9568                 ex.dom = elm;
9569             }else{
9570                 ex = El.cache[el] = new El(elm);
9571             }
9572             return ex;
9573         }else if(el.tagName){ // dom element
9574             if(!(id = el.id)){
9575                 id = Roo.id(el);
9576             }
9577             if(ex = El.cache[id]){
9578                 ex.dom = el;
9579             }else{
9580                 ex = El.cache[id] = new El(el);
9581             }
9582             return ex;
9583         }else if(el instanceof El){
9584             if(el != docEl){
9585                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9586                                                               // catch case where it hasn't been appended
9587                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9588             }
9589             return el;
9590         }else if(el.isComposite){
9591             return el;
9592         }else if(el instanceof Array){
9593             return El.select(el);
9594         }else if(el == document){
9595             // create a bogus element object representing the document object
9596             if(!docEl){
9597                 var f = function(){};
9598                 f.prototype = El.prototype;
9599                 docEl = new f();
9600                 docEl.dom = document;
9601             }
9602             return docEl;
9603         }
9604         return null;
9605     };
9606
9607     // private
9608     El.uncache = function(el){
9609         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9610             if(a[i]){
9611                 delete El.cache[a[i].id || a[i]];
9612             }
9613         }
9614     };
9615
9616     // private
9617     // Garbage collection - uncache elements/purge listeners on orphaned elements
9618     // so we don't hold a reference and cause the browser to retain them
9619     El.garbageCollect = function(){
9620         if(!Roo.enableGarbageCollector){
9621             clearInterval(El.collectorThread);
9622             return;
9623         }
9624         for(var eid in El.cache){
9625             var el = El.cache[eid], d = el.dom;
9626             // -------------------------------------------------------
9627             // Determining what is garbage:
9628             // -------------------------------------------------------
9629             // !d
9630             // dom node is null, definitely garbage
9631             // -------------------------------------------------------
9632             // !d.parentNode
9633             // no parentNode == direct orphan, definitely garbage
9634             // -------------------------------------------------------
9635             // !d.offsetParent && !document.getElementById(eid)
9636             // display none elements have no offsetParent so we will
9637             // also try to look it up by it's id. However, check
9638             // offsetParent first so we don't do unneeded lookups.
9639             // This enables collection of elements that are not orphans
9640             // directly, but somewhere up the line they have an orphan
9641             // parent.
9642             // -------------------------------------------------------
9643             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9644                 delete El.cache[eid];
9645                 if(d && Roo.enableListenerCollection){
9646                     E.purgeElement(d);
9647                 }
9648             }
9649         }
9650     }
9651     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9652
9653
9654     // dom is optional
9655     El.Flyweight = function(dom){
9656         this.dom = dom;
9657     };
9658     El.Flyweight.prototype = El.prototype;
9659
9660     El._flyweights = {};
9661     /**
9662      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9663      * the dom node can be overwritten by other code.
9664      * @param {String/HTMLElement} el The dom node or id
9665      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9666      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9667      * @static
9668      * @return {Element} The shared Element object
9669      */
9670     El.fly = function(el, named){
9671         named = named || '_global';
9672         el = Roo.getDom(el);
9673         if(!el){
9674             return null;
9675         }
9676         if(!El._flyweights[named]){
9677             El._flyweights[named] = new El.Flyweight();
9678         }
9679         El._flyweights[named].dom = el;
9680         return El._flyweights[named];
9681     };
9682
9683     /**
9684      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9685      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9686      * Shorthand of {@link Roo.Element#get}
9687      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9688      * @return {Element} The Element object
9689      * @member Roo
9690      * @method get
9691      */
9692     Roo.get = El.get;
9693     /**
9694      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9695      * the dom node can be overwritten by other code.
9696      * Shorthand of {@link Roo.Element#fly}
9697      * @param {String/HTMLElement} el The dom node or id
9698      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9699      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9700      * @static
9701      * @return {Element} The shared Element object
9702      * @member Roo
9703      * @method fly
9704      */
9705     Roo.fly = El.fly;
9706
9707     // speedy lookup for elements never to box adjust
9708     var noBoxAdjust = Roo.isStrict ? {
9709         select:1
9710     } : {
9711         input:1, select:1, textarea:1
9712     };
9713     if(Roo.isIE || Roo.isGecko){
9714         noBoxAdjust['button'] = 1;
9715     }
9716
9717
9718     Roo.EventManager.on(window, 'unload', function(){
9719         delete El.cache;
9720         delete El._flyweights;
9721     });
9722 })();
9723
9724
9725
9726
9727 if(Roo.DomQuery){
9728     Roo.Element.selectorFunction = Roo.DomQuery.select;
9729 }
9730
9731 Roo.Element.select = function(selector, unique, root){
9732     var els;
9733     if(typeof selector == "string"){
9734         els = Roo.Element.selectorFunction(selector, root);
9735     }else if(selector.length !== undefined){
9736         els = selector;
9737     }else{
9738         throw "Invalid selector";
9739     }
9740     if(unique === true){
9741         return new Roo.CompositeElement(els);
9742     }else{
9743         return new Roo.CompositeElementLite(els);
9744     }
9745 };
9746 /**
9747  * Selects elements based on the passed CSS selector to enable working on them as 1.
9748  * @param {String/Array} selector The CSS selector or an array of elements
9749  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9750  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9751  * @return {CompositeElementLite/CompositeElement}
9752  * @member Roo
9753  * @method select
9754  */
9755 Roo.select = Roo.Element.select;
9756
9757
9758
9759
9760
9761
9762
9763
9764
9765
9766
9767
9768
9769
9770 /*
9771  * Based on:
9772  * Ext JS Library 1.1.1
9773  * Copyright(c) 2006-2007, Ext JS, LLC.
9774  *
9775  * Originally Released Under LGPL - original licence link has changed is not relivant.
9776  *
9777  * Fork - LGPL
9778  * <script type="text/javascript">
9779  */
9780
9781
9782
9783 //Notifies Element that fx methods are available
9784 Roo.enableFx = true;
9785
9786 /**
9787  * @class Roo.Fx
9788  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9789  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9790  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9791  * Element effects to work.</p><br/>
9792  *
9793  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9794  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9795  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9796  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9797  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9798  * expected results and should be done with care.</p><br/>
9799  *
9800  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9801  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9802 <pre>
9803 Value  Description
9804 -----  -----------------------------
9805 tl     The top left corner
9806 t      The center of the top edge
9807 tr     The top right corner
9808 l      The center of the left edge
9809 r      The center of the right edge
9810 bl     The bottom left corner
9811 b      The center of the bottom edge
9812 br     The bottom right corner
9813 </pre>
9814  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9815  * below are common options that can be passed to any Fx method.</b>
9816  * @cfg {Function} callback A function called when the effect is finished
9817  * @cfg {Object} scope The scope of the effect function
9818  * @cfg {String} easing A valid Easing value for the effect
9819  * @cfg {String} afterCls A css class to apply after the effect
9820  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9821  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9822  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9823  * effects that end with the element being visually hidden, ignored otherwise)
9824  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9825  * a function which returns such a specification that will be applied to the Element after the effect finishes
9826  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9827  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9828  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9829  */
9830 Roo.Fx = {
9831         /**
9832          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9833          * origin for the slide effect.  This function automatically handles wrapping the element with
9834          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9835          * Usage:
9836          *<pre><code>
9837 // default: slide the element in from the top
9838 el.slideIn();
9839
9840 // custom: slide the element in from the right with a 2-second duration
9841 el.slideIn('r', { duration: 2 });
9842
9843 // common config options shown with default values
9844 el.slideIn('t', {
9845     easing: 'easeOut',
9846     duration: .5
9847 });
9848 </code></pre>
9849          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9850          * @param {Object} options (optional) Object literal with any of the Fx config options
9851          * @return {Roo.Element} The Element
9852          */
9853     slideIn : function(anchor, o){
9854         var el = this.getFxEl();
9855         o = o || {};
9856
9857         el.queueFx(o, function(){
9858
9859             anchor = anchor || "t";
9860
9861             // fix display to visibility
9862             this.fixDisplay();
9863
9864             // restore values after effect
9865             var r = this.getFxRestore();
9866             var b = this.getBox();
9867             // fixed size for slide
9868             this.setSize(b);
9869
9870             // wrap if needed
9871             var wrap = this.fxWrap(r.pos, o, "hidden");
9872
9873             var st = this.dom.style;
9874             st.visibility = "visible";
9875             st.position = "absolute";
9876
9877             // clear out temp styles after slide and unwrap
9878             var after = function(){
9879                 el.fxUnwrap(wrap, r.pos, o);
9880                 st.width = r.width;
9881                 st.height = r.height;
9882                 el.afterFx(o);
9883             };
9884             // time to calc the positions
9885             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9886
9887             switch(anchor.toLowerCase()){
9888                 case "t":
9889                     wrap.setSize(b.width, 0);
9890                     st.left = st.bottom = "0";
9891                     a = {height: bh};
9892                 break;
9893                 case "l":
9894                     wrap.setSize(0, b.height);
9895                     st.right = st.top = "0";
9896                     a = {width: bw};
9897                 break;
9898                 case "r":
9899                     wrap.setSize(0, b.height);
9900                     wrap.setX(b.right);
9901                     st.left = st.top = "0";
9902                     a = {width: bw, points: pt};
9903                 break;
9904                 case "b":
9905                     wrap.setSize(b.width, 0);
9906                     wrap.setY(b.bottom);
9907                     st.left = st.top = "0";
9908                     a = {height: bh, points: pt};
9909                 break;
9910                 case "tl":
9911                     wrap.setSize(0, 0);
9912                     st.right = st.bottom = "0";
9913                     a = {width: bw, height: bh};
9914                 break;
9915                 case "bl":
9916                     wrap.setSize(0, 0);
9917                     wrap.setY(b.y+b.height);
9918                     st.right = st.top = "0";
9919                     a = {width: bw, height: bh, points: pt};
9920                 break;
9921                 case "br":
9922                     wrap.setSize(0, 0);
9923                     wrap.setXY([b.right, b.bottom]);
9924                     st.left = st.top = "0";
9925                     a = {width: bw, height: bh, points: pt};
9926                 break;
9927                 case "tr":
9928                     wrap.setSize(0, 0);
9929                     wrap.setX(b.x+b.width);
9930                     st.left = st.bottom = "0";
9931                     a = {width: bw, height: bh, points: pt};
9932                 break;
9933             }
9934             this.dom.style.visibility = "visible";
9935             wrap.show();
9936
9937             arguments.callee.anim = wrap.fxanim(a,
9938                 o,
9939                 'motion',
9940                 .5,
9941                 'easeOut', after);
9942         });
9943         return this;
9944     },
9945     
9946         /**
9947          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9948          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9949          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9950          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9951          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9952          * Usage:
9953          *<pre><code>
9954 // default: slide the element out to the top
9955 el.slideOut();
9956
9957 // custom: slide the element out to the right with a 2-second duration
9958 el.slideOut('r', { duration: 2 });
9959
9960 // common config options shown with default values
9961 el.slideOut('t', {
9962     easing: 'easeOut',
9963     duration: .5,
9964     remove: false,
9965     useDisplay: false
9966 });
9967 </code></pre>
9968          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9969          * @param {Object} options (optional) Object literal with any of the Fx config options
9970          * @return {Roo.Element} The Element
9971          */
9972     slideOut : function(anchor, o){
9973         var el = this.getFxEl();
9974         o = o || {};
9975
9976         el.queueFx(o, function(){
9977
9978             anchor = anchor || "t";
9979
9980             // restore values after effect
9981             var r = this.getFxRestore();
9982             
9983             var b = this.getBox();
9984             // fixed size for slide
9985             this.setSize(b);
9986
9987             // wrap if needed
9988             var wrap = this.fxWrap(r.pos, o, "visible");
9989
9990             var st = this.dom.style;
9991             st.visibility = "visible";
9992             st.position = "absolute";
9993
9994             wrap.setSize(b);
9995
9996             var after = function(){
9997                 if(o.useDisplay){
9998                     el.setDisplayed(false);
9999                 }else{
10000                     el.hide();
10001                 }
10002
10003                 el.fxUnwrap(wrap, r.pos, o);
10004
10005                 st.width = r.width;
10006                 st.height = r.height;
10007
10008                 el.afterFx(o);
10009             };
10010
10011             var a, zero = {to: 0};
10012             switch(anchor.toLowerCase()){
10013                 case "t":
10014                     st.left = st.bottom = "0";
10015                     a = {height: zero};
10016                 break;
10017                 case "l":
10018                     st.right = st.top = "0";
10019                     a = {width: zero};
10020                 break;
10021                 case "r":
10022                     st.left = st.top = "0";
10023                     a = {width: zero, points: {to:[b.right, b.y]}};
10024                 break;
10025                 case "b":
10026                     st.left = st.top = "0";
10027                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10028                 break;
10029                 case "tl":
10030                     st.right = st.bottom = "0";
10031                     a = {width: zero, height: zero};
10032                 break;
10033                 case "bl":
10034                     st.right = st.top = "0";
10035                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10036                 break;
10037                 case "br":
10038                     st.left = st.top = "0";
10039                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10040                 break;
10041                 case "tr":
10042                     st.left = st.bottom = "0";
10043                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10044                 break;
10045             }
10046
10047             arguments.callee.anim = wrap.fxanim(a,
10048                 o,
10049                 'motion',
10050                 .5,
10051                 "easeOut", after);
10052         });
10053         return this;
10054     },
10055
10056         /**
10057          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10058          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10059          * The element must be removed from the DOM using the 'remove' config option if desired.
10060          * Usage:
10061          *<pre><code>
10062 // default
10063 el.puff();
10064
10065 // common config options shown with default values
10066 el.puff({
10067     easing: 'easeOut',
10068     duration: .5,
10069     remove: false,
10070     useDisplay: false
10071 });
10072 </code></pre>
10073          * @param {Object} options (optional) Object literal with any of the Fx config options
10074          * @return {Roo.Element} The Element
10075          */
10076     puff : function(o){
10077         var el = this.getFxEl();
10078         o = o || {};
10079
10080         el.queueFx(o, function(){
10081             this.clearOpacity();
10082             this.show();
10083
10084             // restore values after effect
10085             var r = this.getFxRestore();
10086             var st = this.dom.style;
10087
10088             var after = function(){
10089                 if(o.useDisplay){
10090                     el.setDisplayed(false);
10091                 }else{
10092                     el.hide();
10093                 }
10094
10095                 el.clearOpacity();
10096
10097                 el.setPositioning(r.pos);
10098                 st.width = r.width;
10099                 st.height = r.height;
10100                 st.fontSize = '';
10101                 el.afterFx(o);
10102             };
10103
10104             var width = this.getWidth();
10105             var height = this.getHeight();
10106
10107             arguments.callee.anim = this.fxanim({
10108                     width : {to: this.adjustWidth(width * 2)},
10109                     height : {to: this.adjustHeight(height * 2)},
10110                     points : {by: [-(width * .5), -(height * .5)]},
10111                     opacity : {to: 0},
10112                     fontSize: {to:200, unit: "%"}
10113                 },
10114                 o,
10115                 'motion',
10116                 .5,
10117                 "easeOut", after);
10118         });
10119         return this;
10120     },
10121
10122         /**
10123          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10124          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10125          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10126          * Usage:
10127          *<pre><code>
10128 // default
10129 el.switchOff();
10130
10131 // all config options shown with default values
10132 el.switchOff({
10133     easing: 'easeIn',
10134     duration: .3,
10135     remove: false,
10136     useDisplay: false
10137 });
10138 </code></pre>
10139          * @param {Object} options (optional) Object literal with any of the Fx config options
10140          * @return {Roo.Element} The Element
10141          */
10142     switchOff : function(o){
10143         var el = this.getFxEl();
10144         o = o || {};
10145
10146         el.queueFx(o, function(){
10147             this.clearOpacity();
10148             this.clip();
10149
10150             // restore values after effect
10151             var r = this.getFxRestore();
10152             var st = this.dom.style;
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.clearOpacity();
10162                 el.setPositioning(r.pos);
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10170                 this.clearOpacity();
10171                 (function(){
10172                     this.fxanim({
10173                         height:{to:1},
10174                         points:{by:[0, this.getHeight() * .5]}
10175                     }, o, 'motion', 0.3, 'easeIn', after);
10176                 }).defer(100, this);
10177             });
10178         });
10179         return this;
10180     },
10181
10182     /**
10183      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10184      * changed using the "attr" config option) and then fading back to the original color. If no original
10185      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10186      * Usage:
10187 <pre><code>
10188 // default: highlight background to yellow
10189 el.highlight();
10190
10191 // custom: highlight foreground text to blue for 2 seconds
10192 el.highlight("0000ff", { attr: 'color', duration: 2 });
10193
10194 // common config options shown with default values
10195 el.highlight("ffff9c", {
10196     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10197     endColor: (current color) or "ffffff",
10198     easing: 'easeIn',
10199     duration: 1
10200 });
10201 </code></pre>
10202      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10203      * @param {Object} options (optional) Object literal with any of the Fx config options
10204      * @return {Roo.Element} The Element
10205      */ 
10206     highlight : function(color, o){
10207         var el = this.getFxEl();
10208         o = o || {};
10209
10210         el.queueFx(o, function(){
10211             color = color || "ffff9c";
10212             attr = o.attr || "backgroundColor";
10213
10214             this.clearOpacity();
10215             this.show();
10216
10217             var origColor = this.getColor(attr);
10218             var restoreColor = this.dom.style[attr];
10219             endColor = (o.endColor || origColor) || "ffffff";
10220
10221             var after = function(){
10222                 el.dom.style[attr] = restoreColor;
10223                 el.afterFx(o);
10224             };
10225
10226             var a = {};
10227             a[attr] = {from: color, to: endColor};
10228             arguments.callee.anim = this.fxanim(a,
10229                 o,
10230                 'color',
10231                 1,
10232                 'easeIn', after);
10233         });
10234         return this;
10235     },
10236
10237    /**
10238     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10239     * Usage:
10240 <pre><code>
10241 // default: a single light blue ripple
10242 el.frame();
10243
10244 // custom: 3 red ripples lasting 3 seconds total
10245 el.frame("ff0000", 3, { duration: 3 });
10246
10247 // common config options shown with default values
10248 el.frame("C3DAF9", 1, {
10249     duration: 1 //duration of entire animation (not each individual ripple)
10250     // Note: Easing is not configurable and will be ignored if included
10251 });
10252 </code></pre>
10253     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10254     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10255     * @param {Object} options (optional) Object literal with any of the Fx config options
10256     * @return {Roo.Element} The Element
10257     */
10258     frame : function(color, count, o){
10259         var el = this.getFxEl();
10260         o = o || {};
10261
10262         el.queueFx(o, function(){
10263             color = color || "#C3DAF9";
10264             if(color.length == 6){
10265                 color = "#" + color;
10266             }
10267             count = count || 1;
10268             duration = o.duration || 1;
10269             this.show();
10270
10271             var b = this.getBox();
10272             var animFn = function(){
10273                 var proxy = this.createProxy({
10274
10275                      style:{
10276                         visbility:"hidden",
10277                         position:"absolute",
10278                         "z-index":"35000", // yee haw
10279                         border:"0px solid " + color
10280                      }
10281                   });
10282                 var scale = Roo.isBorderBox ? 2 : 1;
10283                 proxy.animate({
10284                     top:{from:b.y, to:b.y - 20},
10285                     left:{from:b.x, to:b.x - 20},
10286                     borderWidth:{from:0, to:10},
10287                     opacity:{from:1, to:0},
10288                     height:{from:b.height, to:(b.height + (20*scale))},
10289                     width:{from:b.width, to:(b.width + (20*scale))}
10290                 }, duration, function(){
10291                     proxy.remove();
10292                 });
10293                 if(--count > 0){
10294                      animFn.defer((duration/2)*1000, this);
10295                 }else{
10296                     el.afterFx(o);
10297                 }
10298             };
10299             animFn.call(this);
10300         });
10301         return this;
10302     },
10303
10304    /**
10305     * Creates a pause before any subsequent queued effects begin.  If there are
10306     * no effects queued after the pause it will have no effect.
10307     * Usage:
10308 <pre><code>
10309 el.pause(1);
10310 </code></pre>
10311     * @param {Number} seconds The length of time to pause (in seconds)
10312     * @return {Roo.Element} The Element
10313     */
10314     pause : function(seconds){
10315         var el = this.getFxEl();
10316         var o = {};
10317
10318         el.queueFx(o, function(){
10319             setTimeout(function(){
10320                 el.afterFx(o);
10321             }, seconds * 1000);
10322         });
10323         return this;
10324     },
10325
10326    /**
10327     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10328     * using the "endOpacity" config option.
10329     * Usage:
10330 <pre><code>
10331 // default: fade in from opacity 0 to 100%
10332 el.fadeIn();
10333
10334 // custom: fade in from opacity 0 to 75% over 2 seconds
10335 el.fadeIn({ endOpacity: .75, duration: 2});
10336
10337 // common config options shown with default values
10338 el.fadeIn({
10339     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10340     easing: 'easeOut',
10341     duration: .5
10342 });
10343 </code></pre>
10344     * @param {Object} options (optional) Object literal with any of the Fx config options
10345     * @return {Roo.Element} The Element
10346     */
10347     fadeIn : function(o){
10348         var el = this.getFxEl();
10349         o = o || {};
10350         el.queueFx(o, function(){
10351             this.setOpacity(0);
10352             this.fixDisplay();
10353             this.dom.style.visibility = 'visible';
10354             var to = o.endOpacity || 1;
10355             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10356                 o, null, .5, "easeOut", function(){
10357                 if(to == 1){
10358                     this.clearOpacity();
10359                 }
10360                 el.afterFx(o);
10361             });
10362         });
10363         return this;
10364     },
10365
10366    /**
10367     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10368     * using the "endOpacity" config option.
10369     * Usage:
10370 <pre><code>
10371 // default: fade out from the element's current opacity to 0
10372 el.fadeOut();
10373
10374 // custom: fade out from the element's current opacity to 25% over 2 seconds
10375 el.fadeOut({ endOpacity: .25, duration: 2});
10376
10377 // common config options shown with default values
10378 el.fadeOut({
10379     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10380     easing: 'easeOut',
10381     duration: .5
10382     remove: false,
10383     useDisplay: false
10384 });
10385 </code></pre>
10386     * @param {Object} options (optional) Object literal with any of the Fx config options
10387     * @return {Roo.Element} The Element
10388     */
10389     fadeOut : function(o){
10390         var el = this.getFxEl();
10391         o = o || {};
10392         el.queueFx(o, function(){
10393             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10394                 o, null, .5, "easeOut", function(){
10395                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10396                      this.dom.style.display = "none";
10397                 }else{
10398                      this.dom.style.visibility = "hidden";
10399                 }
10400                 this.clearOpacity();
10401                 el.afterFx(o);
10402             });
10403         });
10404         return this;
10405     },
10406
10407    /**
10408     * Animates the transition of an element's dimensions from a starting height/width
10409     * to an ending height/width.
10410     * Usage:
10411 <pre><code>
10412 // change height and width to 100x100 pixels
10413 el.scale(100, 100);
10414
10415 // common config options shown with default values.  The height and width will default to
10416 // the element's existing values if passed as null.
10417 el.scale(
10418     [element's width],
10419     [element's height], {
10420     easing: 'easeOut',
10421     duration: .35
10422 });
10423 </code></pre>
10424     * @param {Number} width  The new width (pass undefined to keep the original width)
10425     * @param {Number} height  The new height (pass undefined to keep the original height)
10426     * @param {Object} options (optional) Object literal with any of the Fx config options
10427     * @return {Roo.Element} The Element
10428     */
10429     scale : function(w, h, o){
10430         this.shift(Roo.apply({}, o, {
10431             width: w,
10432             height: h
10433         }));
10434         return this;
10435     },
10436
10437    /**
10438     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10439     * Any of these properties not specified in the config object will not be changed.  This effect 
10440     * requires that at least one new dimension, position or opacity setting must be passed in on
10441     * the config object in order for the function to have any effect.
10442     * Usage:
10443 <pre><code>
10444 // slide the element horizontally to x position 200 while changing the height and opacity
10445 el.shift({ x: 200, height: 50, opacity: .8 });
10446
10447 // common config options shown with default values.
10448 el.shift({
10449     width: [element's width],
10450     height: [element's height],
10451     x: [element's x position],
10452     y: [element's y position],
10453     opacity: [element's opacity],
10454     easing: 'easeOut',
10455     duration: .35
10456 });
10457 </code></pre>
10458     * @param {Object} options  Object literal with any of the Fx config options
10459     * @return {Roo.Element} The Element
10460     */
10461     shift : function(o){
10462         var el = this.getFxEl();
10463         o = o || {};
10464         el.queueFx(o, function(){
10465             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10466             if(w !== undefined){
10467                 a.width = {to: this.adjustWidth(w)};
10468             }
10469             if(h !== undefined){
10470                 a.height = {to: this.adjustHeight(h)};
10471             }
10472             if(x !== undefined || y !== undefined){
10473                 a.points = {to: [
10474                     x !== undefined ? x : this.getX(),
10475                     y !== undefined ? y : this.getY()
10476                 ]};
10477             }
10478             if(op !== undefined){
10479                 a.opacity = {to: op};
10480             }
10481             if(o.xy !== undefined){
10482                 a.points = {to: o.xy};
10483             }
10484             arguments.callee.anim = this.fxanim(a,
10485                 o, 'motion', .35, "easeOut", function(){
10486                 el.afterFx(o);
10487             });
10488         });
10489         return this;
10490     },
10491
10492         /**
10493          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10494          * ending point of the effect.
10495          * Usage:
10496          *<pre><code>
10497 // default: slide the element downward while fading out
10498 el.ghost();
10499
10500 // custom: slide the element out to the right with a 2-second duration
10501 el.ghost('r', { duration: 2 });
10502
10503 // common config options shown with default values
10504 el.ghost('b', {
10505     easing: 'easeOut',
10506     duration: .5
10507     remove: false,
10508     useDisplay: false
10509 });
10510 </code></pre>
10511          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10512          * @param {Object} options (optional) Object literal with any of the Fx config options
10513          * @return {Roo.Element} The Element
10514          */
10515     ghost : function(anchor, o){
10516         var el = this.getFxEl();
10517         o = o || {};
10518
10519         el.queueFx(o, function(){
10520             anchor = anchor || "b";
10521
10522             // restore values after effect
10523             var r = this.getFxRestore();
10524             var w = this.getWidth(),
10525                 h = this.getHeight();
10526
10527             var st = this.dom.style;
10528
10529             var after = function(){
10530                 if(o.useDisplay){
10531                     el.setDisplayed(false);
10532                 }else{
10533                     el.hide();
10534                 }
10535
10536                 el.clearOpacity();
10537                 el.setPositioning(r.pos);
10538                 st.width = r.width;
10539                 st.height = r.height;
10540
10541                 el.afterFx(o);
10542             };
10543
10544             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10545             switch(anchor.toLowerCase()){
10546                 case "t":
10547                     pt.by = [0, -h];
10548                 break;
10549                 case "l":
10550                     pt.by = [-w, 0];
10551                 break;
10552                 case "r":
10553                     pt.by = [w, 0];
10554                 break;
10555                 case "b":
10556                     pt.by = [0, h];
10557                 break;
10558                 case "tl":
10559                     pt.by = [-w, -h];
10560                 break;
10561                 case "bl":
10562                     pt.by = [-w, h];
10563                 break;
10564                 case "br":
10565                     pt.by = [w, h];
10566                 break;
10567                 case "tr":
10568                     pt.by = [w, -h];
10569                 break;
10570             }
10571
10572             arguments.callee.anim = this.fxanim(a,
10573                 o,
10574                 'motion',
10575                 .5,
10576                 "easeOut", after);
10577         });
10578         return this;
10579     },
10580
10581         /**
10582          * Ensures that all effects queued after syncFx is called on the element are
10583          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10584          * @return {Roo.Element} The Element
10585          */
10586     syncFx : function(){
10587         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10588             block : false,
10589             concurrent : true,
10590             stopFx : false
10591         });
10592         return this;
10593     },
10594
10595         /**
10596          * Ensures that all effects queued after sequenceFx is called on the element are
10597          * run in sequence.  This is the opposite of {@link #syncFx}.
10598          * @return {Roo.Element} The Element
10599          */
10600     sequenceFx : function(){
10601         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10602             block : false,
10603             concurrent : false,
10604             stopFx : false
10605         });
10606         return this;
10607     },
10608
10609         /* @private */
10610     nextFx : function(){
10611         var ef = this.fxQueue[0];
10612         if(ef){
10613             ef.call(this);
10614         }
10615     },
10616
10617         /**
10618          * Returns true if the element has any effects actively running or queued, else returns false.
10619          * @return {Boolean} True if element has active effects, else false
10620          */
10621     hasActiveFx : function(){
10622         return this.fxQueue && this.fxQueue[0];
10623     },
10624
10625         /**
10626          * Stops any running effects and clears the element's internal effects queue if it contains
10627          * any additional effects that haven't started yet.
10628          * @return {Roo.Element} The Element
10629          */
10630     stopFx : function(){
10631         if(this.hasActiveFx()){
10632             var cur = this.fxQueue[0];
10633             if(cur && cur.anim && cur.anim.isAnimated()){
10634                 this.fxQueue = [cur]; // clear out others
10635                 cur.anim.stop(true);
10636             }
10637         }
10638         return this;
10639     },
10640
10641         /* @private */
10642     beforeFx : function(o){
10643         if(this.hasActiveFx() && !o.concurrent){
10644            if(o.stopFx){
10645                this.stopFx();
10646                return true;
10647            }
10648            return false;
10649         }
10650         return true;
10651     },
10652
10653         /**
10654          * Returns true if the element is currently blocking so that no other effect can be queued
10655          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10656          * used to ensure that an effect initiated by a user action runs to completion prior to the
10657          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10658          * @return {Boolean} True if blocking, else false
10659          */
10660     hasFxBlock : function(){
10661         var q = this.fxQueue;
10662         return q && q[0] && q[0].block;
10663     },
10664
10665         /* @private */
10666     queueFx : function(o, fn){
10667         if(!this.fxQueue){
10668             this.fxQueue = [];
10669         }
10670         if(!this.hasFxBlock()){
10671             Roo.applyIf(o, this.fxDefaults);
10672             if(!o.concurrent){
10673                 var run = this.beforeFx(o);
10674                 fn.block = o.block;
10675                 this.fxQueue.push(fn);
10676                 if(run){
10677                     this.nextFx();
10678                 }
10679             }else{
10680                 fn.call(this);
10681             }
10682         }
10683         return this;
10684     },
10685
10686         /* @private */
10687     fxWrap : function(pos, o, vis){
10688         var wrap;
10689         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10690             var wrapXY;
10691             if(o.fixPosition){
10692                 wrapXY = this.getXY();
10693             }
10694             var div = document.createElement("div");
10695             div.style.visibility = vis;
10696             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10697             wrap.setPositioning(pos);
10698             if(wrap.getStyle("position") == "static"){
10699                 wrap.position("relative");
10700             }
10701             this.clearPositioning('auto');
10702             wrap.clip();
10703             wrap.dom.appendChild(this.dom);
10704             if(wrapXY){
10705                 wrap.setXY(wrapXY);
10706             }
10707         }
10708         return wrap;
10709     },
10710
10711         /* @private */
10712     fxUnwrap : function(wrap, pos, o){
10713         this.clearPositioning();
10714         this.setPositioning(pos);
10715         if(!o.wrap){
10716             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10717             wrap.remove();
10718         }
10719     },
10720
10721         /* @private */
10722     getFxRestore : function(){
10723         var st = this.dom.style;
10724         return {pos: this.getPositioning(), width: st.width, height : st.height};
10725     },
10726
10727         /* @private */
10728     afterFx : function(o){
10729         if(o.afterStyle){
10730             this.applyStyles(o.afterStyle);
10731         }
10732         if(o.afterCls){
10733             this.addClass(o.afterCls);
10734         }
10735         if(o.remove === true){
10736             this.remove();
10737         }
10738         Roo.callback(o.callback, o.scope, [this]);
10739         if(!o.concurrent){
10740             this.fxQueue.shift();
10741             this.nextFx();
10742         }
10743     },
10744
10745         /* @private */
10746     getFxEl : function(){ // support for composite element fx
10747         return Roo.get(this.dom);
10748     },
10749
10750         /* @private */
10751     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10752         animType = animType || 'run';
10753         opt = opt || {};
10754         var anim = Roo.lib.Anim[animType](
10755             this.dom, args,
10756             (opt.duration || defaultDur) || .35,
10757             (opt.easing || defaultEase) || 'easeOut',
10758             function(){
10759                 Roo.callback(cb, this);
10760             },
10761             this
10762         );
10763         opt.anim = anim;
10764         return anim;
10765     }
10766 };
10767
10768 // backwords compat
10769 Roo.Fx.resize = Roo.Fx.scale;
10770
10771 //When included, Roo.Fx is automatically applied to Element so that all basic
10772 //effects are available directly via the Element API
10773 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10774  * Based on:
10775  * Ext JS Library 1.1.1
10776  * Copyright(c) 2006-2007, Ext JS, LLC.
10777  *
10778  * Originally Released Under LGPL - original licence link has changed is not relivant.
10779  *
10780  * Fork - LGPL
10781  * <script type="text/javascript">
10782  */
10783
10784
10785 /**
10786  * @class Roo.CompositeElement
10787  * Standard composite class. Creates a Roo.Element for every element in the collection.
10788  * <br><br>
10789  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10790  * actions will be performed on all the elements in this collection.</b>
10791  * <br><br>
10792  * All methods return <i>this</i> and can be chained.
10793  <pre><code>
10794  var els = Roo.select("#some-el div.some-class", true);
10795  // or select directly from an existing element
10796  var el = Roo.get('some-el');
10797  el.select('div.some-class', true);
10798
10799  els.setWidth(100); // all elements become 100 width
10800  els.hide(true); // all elements fade out and hide
10801  // or
10802  els.setWidth(100).hide(true);
10803  </code></pre>
10804  */
10805 Roo.CompositeElement = function(els){
10806     this.elements = [];
10807     this.addElements(els);
10808 };
10809 Roo.CompositeElement.prototype = {
10810     isComposite: true,
10811     addElements : function(els){
10812         if(!els) return this;
10813         if(typeof els == "string"){
10814             els = Roo.Element.selectorFunction(els);
10815         }
10816         var yels = this.elements;
10817         var index = yels.length-1;
10818         for(var i = 0, len = els.length; i < len; i++) {
10819                 yels[++index] = Roo.get(els[i]);
10820         }
10821         return this;
10822     },
10823
10824     /**
10825     * Clears this composite and adds the elements returned by the passed selector.
10826     * @param {String/Array} els A string CSS selector, an array of elements or an element
10827     * @return {CompositeElement} this
10828     */
10829     fill : function(els){
10830         this.elements = [];
10831         this.add(els);
10832         return this;
10833     },
10834
10835     /**
10836     * Filters this composite to only elements that match the passed selector.
10837     * @param {String} selector A string CSS selector
10838     * @return {CompositeElement} this
10839     */
10840     filter : function(selector){
10841         var els = [];
10842         this.each(function(el){
10843             if(el.is(selector)){
10844                 els[els.length] = el.dom;
10845             }
10846         });
10847         this.fill(els);
10848         return this;
10849     },
10850
10851     invoke : function(fn, args){
10852         var els = this.elements;
10853         for(var i = 0, len = els.length; i < len; i++) {
10854                 Roo.Element.prototype[fn].apply(els[i], args);
10855         }
10856         return this;
10857     },
10858     /**
10859     * Adds elements to this composite.
10860     * @param {String/Array} els A string CSS selector, an array of elements or an element
10861     * @return {CompositeElement} this
10862     */
10863     add : function(els){
10864         if(typeof els == "string"){
10865             this.addElements(Roo.Element.selectorFunction(els));
10866         }else if(els.length !== undefined){
10867             this.addElements(els);
10868         }else{
10869             this.addElements([els]);
10870         }
10871         return this;
10872     },
10873     /**
10874     * Calls the passed function passing (el, this, index) for each element in this composite.
10875     * @param {Function} fn The function to call
10876     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10877     * @return {CompositeElement} this
10878     */
10879     each : function(fn, scope){
10880         var els = this.elements;
10881         for(var i = 0, len = els.length; i < len; i++){
10882             if(fn.call(scope || els[i], els[i], this, i) === false) {
10883                 break;
10884             }
10885         }
10886         return this;
10887     },
10888
10889     /**
10890      * Returns the Element object at the specified index
10891      * @param {Number} index
10892      * @return {Roo.Element}
10893      */
10894     item : function(index){
10895         return this.elements[index] || null;
10896     },
10897
10898     /**
10899      * Returns the first Element
10900      * @return {Roo.Element}
10901      */
10902     first : function(){
10903         return this.item(0);
10904     },
10905
10906     /**
10907      * Returns the last Element
10908      * @return {Roo.Element}
10909      */
10910     last : function(){
10911         return this.item(this.elements.length-1);
10912     },
10913
10914     /**
10915      * Returns the number of elements in this composite
10916      * @return Number
10917      */
10918     getCount : function(){
10919         return this.elements.length;
10920     },
10921
10922     /**
10923      * Returns true if this composite contains the passed element
10924      * @return Boolean
10925      */
10926     contains : function(el){
10927         return this.indexOf(el) !== -1;
10928     },
10929
10930     /**
10931      * Returns true if this composite contains the passed element
10932      * @return Boolean
10933      */
10934     indexOf : function(el){
10935         return this.elements.indexOf(Roo.get(el));
10936     },
10937
10938
10939     /**
10940     * Removes the specified element(s).
10941     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10942     * or an array of any of those.
10943     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10944     * @return {CompositeElement} this
10945     */
10946     removeElement : function(el, removeDom){
10947         if(el instanceof Array){
10948             for(var i = 0, len = el.length; i < len; i++){
10949                 this.removeElement(el[i]);
10950             }
10951             return this;
10952         }
10953         var index = typeof el == 'number' ? el : this.indexOf(el);
10954         if(index !== -1){
10955             if(removeDom){
10956                 var d = this.elements[index];
10957                 if(d.dom){
10958                     d.remove();
10959                 }else{
10960                     d.parentNode.removeChild(d);
10961                 }
10962             }
10963             this.elements.splice(index, 1);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Replaces the specified element with the passed element.
10970     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10971     * to replace.
10972     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10973     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10974     * @return {CompositeElement} this
10975     */
10976     replaceElement : function(el, replacement, domReplace){
10977         var index = typeof el == 'number' ? el : this.indexOf(el);
10978         if(index !== -1){
10979             if(domReplace){
10980                 this.elements[index].replaceWith(replacement);
10981             }else{
10982                 this.elements.splice(index, 1, Roo.get(replacement))
10983             }
10984         }
10985         return this;
10986     },
10987
10988     /**
10989      * Removes all elements.
10990      */
10991     clear : function(){
10992         this.elements = [];
10993     }
10994 };
10995 (function(){
10996     Roo.CompositeElement.createCall = function(proto, fnName){
10997         if(!proto[fnName]){
10998             proto[fnName] = function(){
10999                 return this.invoke(fnName, arguments);
11000             };
11001         }
11002     };
11003     for(var fnName in Roo.Element.prototype){
11004         if(typeof Roo.Element.prototype[fnName] == "function"){
11005             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11006         }
11007     };
11008 })();
11009 /*
11010  * Based on:
11011  * Ext JS Library 1.1.1
11012  * Copyright(c) 2006-2007, Ext JS, LLC.
11013  *
11014  * Originally Released Under LGPL - original licence link has changed is not relivant.
11015  *
11016  * Fork - LGPL
11017  * <script type="text/javascript">
11018  */
11019
11020 /**
11021  * @class Roo.CompositeElementLite
11022  * @extends Roo.CompositeElement
11023  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11024  <pre><code>
11025  var els = Roo.select("#some-el div.some-class");
11026  // or select directly from an existing element
11027  var el = Roo.get('some-el');
11028  el.select('div.some-class');
11029
11030  els.setWidth(100); // all elements become 100 width
11031  els.hide(true); // all elements fade out and hide
11032  // or
11033  els.setWidth(100).hide(true);
11034  </code></pre><br><br>
11035  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11036  * actions will be performed on all the elements in this collection.</b>
11037  */
11038 Roo.CompositeElementLite = function(els){
11039     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11040     this.el = new Roo.Element.Flyweight();
11041 };
11042 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11043     addElements : function(els){
11044         if(els){
11045             if(els instanceof Array){
11046                 this.elements = this.elements.concat(els);
11047             }else{
11048                 var yels = this.elements;
11049                 var index = yels.length-1;
11050                 for(var i = 0, len = els.length; i < len; i++) {
11051                     yels[++index] = els[i];
11052                 }
11053             }
11054         }
11055         return this;
11056     },
11057     invoke : function(fn, args){
11058         var els = this.elements;
11059         var el = this.el;
11060         for(var i = 0, len = els.length; i < len; i++) {
11061             el.dom = els[i];
11062                 Roo.Element.prototype[fn].apply(el, args);
11063         }
11064         return this;
11065     },
11066     /**
11067      * Returns a flyweight Element of the dom element object at the specified index
11068      * @param {Number} index
11069      * @return {Roo.Element}
11070      */
11071     item : function(index){
11072         if(!this.elements[index]){
11073             return null;
11074         }
11075         this.el.dom = this.elements[index];
11076         return this.el;
11077     },
11078
11079     // fixes scope with flyweight
11080     addListener : function(eventName, handler, scope, opt){
11081         var els = this.elements;
11082         for(var i = 0, len = els.length; i < len; i++) {
11083             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11084         }
11085         return this;
11086     },
11087
11088     /**
11089     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11090     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11091     * a reference to the dom node, use el.dom.</b>
11092     * @param {Function} fn The function to call
11093     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11094     * @return {CompositeElement} this
11095     */
11096     each : function(fn, scope){
11097         var els = this.elements;
11098         var el = this.el;
11099         for(var i = 0, len = els.length; i < len; i++){
11100             el.dom = els[i];
11101                 if(fn.call(scope || el, el, this, i) === false){
11102                 break;
11103             }
11104         }
11105         return this;
11106     },
11107
11108     indexOf : function(el){
11109         return this.elements.indexOf(Roo.getDom(el));
11110     },
11111
11112     replaceElement : function(el, replacement, domReplace){
11113         var index = typeof el == 'number' ? el : this.indexOf(el);
11114         if(index !== -1){
11115             replacement = Roo.getDom(replacement);
11116             if(domReplace){
11117                 var d = this.elements[index];
11118                 d.parentNode.insertBefore(replacement, d);
11119                 d.parentNode.removeChild(d);
11120             }
11121             this.elements.splice(index, 1, replacement);
11122         }
11123         return this;
11124     }
11125 });
11126 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11127
11128 /*
11129  * Based on:
11130  * Ext JS Library 1.1.1
11131  * Copyright(c) 2006-2007, Ext JS, LLC.
11132  *
11133  * Originally Released Under LGPL - original licence link has changed is not relivant.
11134  *
11135  * Fork - LGPL
11136  * <script type="text/javascript">
11137  */
11138
11139  
11140
11141 /**
11142  * @class Roo.data.Connection
11143  * @extends Roo.util.Observable
11144  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11145  * either to a configured URL, or to a URL specified at request time.<br><br>
11146  * <p>
11147  * Requests made by this class are asynchronous, and will return immediately. No data from
11148  * the server will be available to the statement immediately following the {@link #request} call.
11149  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11150  * <p>
11151  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11152  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11153  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11154  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11155  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11156  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11157  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11158  * standard DOM methods.
11159  * @constructor
11160  * @param {Object} config a configuration object.
11161  */
11162 Roo.data.Connection = function(config){
11163     Roo.apply(this, config);
11164     this.addEvents({
11165         /**
11166          * @event beforerequest
11167          * Fires before a network request is made to retrieve a data object.
11168          * @param {Connection} conn This Connection object.
11169          * @param {Object} options The options config object passed to the {@link #request} method.
11170          */
11171         "beforerequest" : true,
11172         /**
11173          * @event requestcomplete
11174          * Fires if the request was successfully completed.
11175          * @param {Connection} conn This Connection object.
11176          * @param {Object} response The XHR object containing the response data.
11177          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11178          * @param {Object} options The options config object passed to the {@link #request} method.
11179          */
11180         "requestcomplete" : true,
11181         /**
11182          * @event requestexception
11183          * Fires if an error HTTP status was returned from the server.
11184          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11185          * @param {Connection} conn This Connection object.
11186          * @param {Object} response The XHR object containing the response data.
11187          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11188          * @param {Object} options The options config object passed to the {@link #request} method.
11189          */
11190         "requestexception" : true
11191     });
11192     Roo.data.Connection.superclass.constructor.call(this);
11193 };
11194
11195 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11196     /**
11197      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11198      */
11199     /**
11200      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11201      * extra parameters to each request made by this object. (defaults to undefined)
11202      */
11203     /**
11204      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11205      *  to each request made by this object. (defaults to undefined)
11206      */
11207     /**
11208      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11209      */
11210     /**
11211      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11212      */
11213     timeout : 30000,
11214     /**
11215      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11216      * @type Boolean
11217      */
11218     autoAbort:false,
11219
11220     /**
11221      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11222      * @type Boolean
11223      */
11224     disableCaching: true,
11225
11226     /**
11227      * Sends an HTTP request to a remote server.
11228      * @param {Object} options An object which may contain the following properties:<ul>
11229      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11230      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11231      * request, a url encoded string or a function to call to get either.</li>
11232      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11233      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11234      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11235      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11236      * <li>options {Object} The parameter to the request call.</li>
11237      * <li>success {Boolean} True if the request succeeded.</li>
11238      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11239      * </ul></li>
11240      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11241      * The callback is passed the following parameters:<ul>
11242      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11243      * <li>options {Object} The parameter to the request call.</li>
11244      * </ul></li>
11245      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11246      * The callback is passed the following parameters:<ul>
11247      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11248      * <li>options {Object} The parameter to the request call.</li>
11249      * </ul></li>
11250      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11251      * for the callback function. Defaults to the browser window.</li>
11252      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11253      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11254      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11255      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11256      * params for the post data. Any params will be appended to the URL.</li>
11257      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11258      * </ul>
11259      * @return {Number} transactionId
11260      */
11261     request : function(o){
11262         if(this.fireEvent("beforerequest", this, o) !== false){
11263             var p = o.params;
11264
11265             if(typeof p == "function"){
11266                 p = p.call(o.scope||window, o);
11267             }
11268             if(typeof p == "object"){
11269                 p = Roo.urlEncode(o.params);
11270             }
11271             if(this.extraParams){
11272                 var extras = Roo.urlEncode(this.extraParams);
11273                 p = p ? (p + '&' + extras) : extras;
11274             }
11275
11276             var url = o.url || this.url;
11277             if(typeof url == 'function'){
11278                 url = url.call(o.scope||window, o);
11279             }
11280
11281             if(o.form){
11282                 var form = Roo.getDom(o.form);
11283                 url = url || form.action;
11284
11285                 var enctype = form.getAttribute("enctype");
11286                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11287                     return this.doFormUpload(o, p, url);
11288                 }
11289                 var f = Roo.lib.Ajax.serializeForm(form);
11290                 p = p ? (p + '&' + f) : f;
11291             }
11292
11293             var hs = o.headers;
11294             if(this.defaultHeaders){
11295                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11296                 if(!o.headers){
11297                     o.headers = hs;
11298                 }
11299             }
11300
11301             var cb = {
11302                 success: this.handleResponse,
11303                 failure: this.handleFailure,
11304                 scope: this,
11305                 argument: {options: o},
11306                 timeout : this.timeout
11307             };
11308
11309             var method = o.method||this.method||(p ? "POST" : "GET");
11310
11311             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11312                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11313             }
11314
11315             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11316                 if(o.autoAbort){
11317                     this.abort();
11318                 }
11319             }else if(this.autoAbort !== false){
11320                 this.abort();
11321             }
11322
11323             if((method == 'GET' && p) || o.xmlData){
11324                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11325                 p = '';
11326             }
11327             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11328             return this.transId;
11329         }else{
11330             Roo.callback(o.callback, o.scope, [o, null, null]);
11331             return null;
11332         }
11333     },
11334
11335     /**
11336      * Determine whether this object has a request outstanding.
11337      * @param {Number} transactionId (Optional) defaults to the last transaction
11338      * @return {Boolean} True if there is an outstanding request.
11339      */
11340     isLoading : function(transId){
11341         if(transId){
11342             return Roo.lib.Ajax.isCallInProgress(transId);
11343         }else{
11344             return this.transId ? true : false;
11345         }
11346     },
11347
11348     /**
11349      * Aborts any outstanding request.
11350      * @param {Number} transactionId (Optional) defaults to the last transaction
11351      */
11352     abort : function(transId){
11353         if(transId || this.isLoading()){
11354             Roo.lib.Ajax.abort(transId || this.transId);
11355         }
11356     },
11357
11358     // private
11359     handleResponse : function(response){
11360         this.transId = false;
11361         var options = response.argument.options;
11362         response.argument = options ? options.argument : null;
11363         this.fireEvent("requestcomplete", this, response, options);
11364         Roo.callback(options.success, options.scope, [response, options]);
11365         Roo.callback(options.callback, options.scope, [options, true, response]);
11366     },
11367
11368     // private
11369     handleFailure : function(response, e){
11370         this.transId = false;
11371         var options = response.argument.options;
11372         response.argument = options ? options.argument : null;
11373         this.fireEvent("requestexception", this, response, options, e);
11374         Roo.callback(options.failure, options.scope, [response, options]);
11375         Roo.callback(options.callback, options.scope, [options, false, response]);
11376     },
11377
11378     // private
11379     doFormUpload : function(o, ps, url){
11380         var id = Roo.id();
11381         var frame = document.createElement('iframe');
11382         frame.id = id;
11383         frame.name = id;
11384         frame.className = 'x-hidden';
11385         if(Roo.isIE){
11386             frame.src = Roo.SSL_SECURE_URL;
11387         }
11388         document.body.appendChild(frame);
11389
11390         if(Roo.isIE){
11391            document.frames[id].name = id;
11392         }
11393
11394         var form = Roo.getDom(o.form);
11395         form.target = id;
11396         form.method = 'POST';
11397         form.enctype = form.encoding = 'multipart/form-data';
11398         if(url){
11399             form.action = url;
11400         }
11401
11402         var hiddens, hd;
11403         if(ps){ // add dynamic params
11404             hiddens = [];
11405             ps = Roo.urlDecode(ps, false);
11406             for(var k in ps){
11407                 if(ps.hasOwnProperty(k)){
11408                     hd = document.createElement('input');
11409                     hd.type = 'hidden';
11410                     hd.name = k;
11411                     hd.value = ps[k];
11412                     form.appendChild(hd);
11413                     hiddens.push(hd);
11414                 }
11415             }
11416         }
11417
11418         function cb(){
11419             var r = {  // bogus response object
11420                 responseText : '',
11421                 responseXML : null
11422             };
11423
11424             r.argument = o ? o.argument : null;
11425
11426             try { //
11427                 var doc;
11428                 if(Roo.isIE){
11429                     doc = frame.contentWindow.document;
11430                 }else {
11431                     doc = (frame.contentDocument || window.frames[id].document);
11432                 }
11433                 if(doc && doc.body){
11434                     r.responseText = doc.body.innerHTML;
11435                 }
11436                 if(doc && doc.XMLDocument){
11437                     r.responseXML = doc.XMLDocument;
11438                 }else {
11439                     r.responseXML = doc;
11440                 }
11441             }
11442             catch(e) {
11443                 // ignore
11444             }
11445
11446             Roo.EventManager.removeListener(frame, 'load', cb, this);
11447
11448             this.fireEvent("requestcomplete", this, r, o);
11449             Roo.callback(o.success, o.scope, [r, o]);
11450             Roo.callback(o.callback, o.scope, [o, true, r]);
11451
11452             setTimeout(function(){document.body.removeChild(frame);}, 100);
11453         }
11454
11455         Roo.EventManager.on(frame, 'load', cb, this);
11456         form.submit();
11457
11458         if(hiddens){ // remove dynamic params
11459             for(var i = 0, len = hiddens.length; i < len; i++){
11460                 form.removeChild(hiddens[i]);
11461             }
11462         }
11463     }
11464 });
11465
11466 /**
11467  * @class Roo.Ajax
11468  * @extends Roo.data.Connection
11469  * Global Ajax request class.
11470  *
11471  * @singleton
11472  */
11473 Roo.Ajax = new Roo.data.Connection({
11474     // fix up the docs
11475    /**
11476      * @cfg {String} url @hide
11477      */
11478     /**
11479      * @cfg {Object} extraParams @hide
11480      */
11481     /**
11482      * @cfg {Object} defaultHeaders @hide
11483      */
11484     /**
11485      * @cfg {String} method (Optional) @hide
11486      */
11487     /**
11488      * @cfg {Number} timeout (Optional) @hide
11489      */
11490     /**
11491      * @cfg {Boolean} autoAbort (Optional) @hide
11492      */
11493
11494     /**
11495      * @cfg {Boolean} disableCaching (Optional) @hide
11496      */
11497
11498     /**
11499      * @property  disableCaching
11500      * True to add a unique cache-buster param to GET requests. (defaults to true)
11501      * @type Boolean
11502      */
11503     /**
11504      * @property  url
11505      * The default URL to be used for requests to the server. (defaults to undefined)
11506      * @type String
11507      */
11508     /**
11509      * @property  extraParams
11510      * An object containing properties which are used as
11511      * extra parameters to each request made by this object. (defaults to undefined)
11512      * @type Object
11513      */
11514     /**
11515      * @property  defaultHeaders
11516      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11517      * @type Object
11518      */
11519     /**
11520      * @property  method
11521      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11522      * @type String
11523      */
11524     /**
11525      * @property  timeout
11526      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11527      * @type Number
11528      */
11529
11530     /**
11531      * @property  autoAbort
11532      * Whether a new request should abort any pending requests. (defaults to false)
11533      * @type Boolean
11534      */
11535     autoAbort : false,
11536
11537     /**
11538      * Serialize the passed form into a url encoded string
11539      * @param {String/HTMLElement} form
11540      * @return {String}
11541      */
11542     serializeForm : function(form){
11543         return Roo.lib.Ajax.serializeForm(form);
11544     }
11545 });/*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555  
11556 /**
11557  * @class Roo.Ajax
11558  * @extends Roo.data.Connection
11559  * Global Ajax request class.
11560  *
11561  * @instanceOf  Roo.data.Connection
11562  */
11563 Roo.Ajax = new Roo.data.Connection({
11564     // fix up the docs
11565     
11566     /**
11567      * fix up scoping
11568      * @scope Roo.Ajax
11569      */
11570     
11571    /**
11572      * @cfg {String} url @hide
11573      */
11574     /**
11575      * @cfg {Object} extraParams @hide
11576      */
11577     /**
11578      * @cfg {Object} defaultHeaders @hide
11579      */
11580     /**
11581      * @cfg {String} method (Optional) @hide
11582      */
11583     /**
11584      * @cfg {Number} timeout (Optional) @hide
11585      */
11586     /**
11587      * @cfg {Boolean} autoAbort (Optional) @hide
11588      */
11589
11590     /**
11591      * @cfg {Boolean} disableCaching (Optional) @hide
11592      */
11593
11594     /**
11595      * @property  disableCaching
11596      * True to add a unique cache-buster param to GET requests. (defaults to true)
11597      * @type Boolean
11598      */
11599     /**
11600      * @property  url
11601      * The default URL to be used for requests to the server. (defaults to undefined)
11602      * @type String
11603      */
11604     /**
11605      * @property  extraParams
11606      * An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      * @type Object
11609      */
11610     /**
11611      * @property  defaultHeaders
11612      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11613      * @type Object
11614      */
11615     /**
11616      * @property  method
11617      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11618      * @type String
11619      */
11620     /**
11621      * @property  timeout
11622      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11623      * @type Number
11624      */
11625
11626     /**
11627      * @property  autoAbort
11628      * Whether a new request should abort any pending requests. (defaults to false)
11629      * @type Boolean
11630      */
11631     autoAbort : false,
11632
11633     /**
11634      * Serialize the passed form into a url encoded string
11635      * @param {String/HTMLElement} form
11636      * @return {String}
11637      */
11638     serializeForm : function(form){
11639         return Roo.lib.Ajax.serializeForm(form);
11640     }
11641 });/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651
11652  
11653 /**
11654  * @class Roo.UpdateManager
11655  * @extends Roo.util.Observable
11656  * Provides AJAX-style update for Element object.<br><br>
11657  * Usage:<br>
11658  * <pre><code>
11659  * // Get it from a Roo.Element object
11660  * var el = Roo.get("foo");
11661  * var mgr = el.getUpdateManager();
11662  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11663  * ...
11664  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11665  * <br>
11666  * // or directly (returns the same UpdateManager instance)
11667  * var mgr = new Roo.UpdateManager("myElementId");
11668  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11669  * mgr.on("update", myFcnNeedsToKnow);
11670  * <br>
11671    // short handed call directly from the element object
11672    Roo.get("foo").load({
11673         url: "bar.php",
11674         scripts:true,
11675         params: "for=bar",
11676         text: "Loading Foo..."
11677    });
11678  * </code></pre>
11679  * @constructor
11680  * Create new UpdateManager directly.
11681  * @param {String/HTMLElement/Roo.Element} el The element to update
11682  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11683  */
11684 Roo.UpdateManager = function(el, forceNew){
11685     el = Roo.get(el);
11686     if(!forceNew && el.updateManager){
11687         return el.updateManager;
11688     }
11689     /**
11690      * The Element object
11691      * @type Roo.Element
11692      */
11693     this.el = el;
11694     /**
11695      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11696      * @type String
11697      */
11698     this.defaultUrl = null;
11699
11700     this.addEvents({
11701         /**
11702          * @event beforeupdate
11703          * Fired before an update is made, return false from your handler and the update is cancelled.
11704          * @param {Roo.Element} el
11705          * @param {String/Object/Function} url
11706          * @param {String/Object} params
11707          */
11708         "beforeupdate": true,
11709         /**
11710          * @event update
11711          * Fired after successful update is made.
11712          * @param {Roo.Element} el
11713          * @param {Object} oResponseObject The response Object
11714          */
11715         "update": true,
11716         /**
11717          * @event failure
11718          * Fired on update failure.
11719          * @param {Roo.Element} el
11720          * @param {Object} oResponseObject The response Object
11721          */
11722         "failure": true
11723     });
11724     var d = Roo.UpdateManager.defaults;
11725     /**
11726      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11727      * @type String
11728      */
11729     this.sslBlankUrl = d.sslBlankUrl;
11730     /**
11731      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11732      * @type Boolean
11733      */
11734     this.disableCaching = d.disableCaching;
11735     /**
11736      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11737      * @type String
11738      */
11739     this.indicatorText = d.indicatorText;
11740     /**
11741      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11742      * @type String
11743      */
11744     this.showLoadIndicator = d.showLoadIndicator;
11745     /**
11746      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11747      * @type Number
11748      */
11749     this.timeout = d.timeout;
11750
11751     /**
11752      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11753      * @type Boolean
11754      */
11755     this.loadScripts = d.loadScripts;
11756
11757     /**
11758      * Transaction object of current executing transaction
11759      */
11760     this.transaction = null;
11761
11762     /**
11763      * @private
11764      */
11765     this.autoRefreshProcId = null;
11766     /**
11767      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11768      * @type Function
11769      */
11770     this.refreshDelegate = this.refresh.createDelegate(this);
11771     /**
11772      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11773      * @type Function
11774      */
11775     this.updateDelegate = this.update.createDelegate(this);
11776     /**
11777      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11778      * @type Function
11779      */
11780     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11781     /**
11782      * @private
11783      */
11784     this.successDelegate = this.processSuccess.createDelegate(this);
11785     /**
11786      * @private
11787      */
11788     this.failureDelegate = this.processFailure.createDelegate(this);
11789
11790     if(!this.renderer){
11791      /**
11792       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11793       */
11794     this.renderer = new Roo.UpdateManager.BasicRenderer();
11795     }
11796     
11797     Roo.UpdateManager.superclass.constructor.call(this);
11798 };
11799
11800 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11801     /**
11802      * Get the Element this UpdateManager is bound to
11803      * @return {Roo.Element} The element
11804      */
11805     getEl : function(){
11806         return this.el;
11807     },
11808     /**
11809      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11810      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11811 <pre><code>
11812 um.update({<br/>
11813     url: "your-url.php",<br/>
11814     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11815     callback: yourFunction,<br/>
11816     scope: yourObject, //(optional scope)  <br/>
11817     discardUrl: false, <br/>
11818     nocache: false,<br/>
11819     text: "Loading...",<br/>
11820     timeout: 30,<br/>
11821     scripts: false<br/>
11822 });
11823 </code></pre>
11824      * The only required property is url. The optional properties nocache, text and scripts
11825      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11826      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11827      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11828      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11829      */
11830     update : function(url, params, callback, discardUrl){
11831         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11832             var method = this.method, cfg;
11833             if(typeof url == "object"){ // must be config object
11834                 cfg = url;
11835                 url = cfg.url;
11836                 params = params || cfg.params;
11837                 callback = callback || cfg.callback;
11838                 discardUrl = discardUrl || cfg.discardUrl;
11839                 if(callback && cfg.scope){
11840                     callback = callback.createDelegate(cfg.scope);
11841                 }
11842                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11843                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11844                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11845                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11846                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11847             }
11848             this.showLoading();
11849             if(!discardUrl){
11850                 this.defaultUrl = url;
11851             }
11852             if(typeof url == "function"){
11853                 url = url.call(this);
11854             }
11855
11856             method = method || (params ? "POST" : "GET");
11857             if(method == "GET"){
11858                 url = this.prepareUrl(url);
11859             }
11860
11861             var o = Roo.apply(cfg ||{}, {
11862                 url : url,
11863                 params: params,
11864                 success: this.successDelegate,
11865                 failure: this.failureDelegate,
11866                 callback: undefined,
11867                 timeout: (this.timeout*1000),
11868                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11869             });
11870
11871             this.transaction = Roo.Ajax.request(o);
11872         }
11873     },
11874
11875     /**
11876      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11877      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11878      * @param {String/HTMLElement} form The form Id or form element
11879      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11880      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11881      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11882      */
11883     formUpdate : function(form, url, reset, callback){
11884         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11885             if(typeof url == "function"){
11886                 url = url.call(this);
11887             }
11888             form = Roo.getDom(form);
11889             this.transaction = Roo.Ajax.request({
11890                 form: form,
11891                 url:url,
11892                 success: this.successDelegate,
11893                 failure: this.failureDelegate,
11894                 timeout: (this.timeout*1000),
11895                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11896             });
11897             this.showLoading.defer(1, this);
11898         }
11899     },
11900
11901     /**
11902      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11903      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11904      */
11905     refresh : function(callback){
11906         if(this.defaultUrl == null){
11907             return;
11908         }
11909         this.update(this.defaultUrl, null, callback, true);
11910     },
11911
11912     /**
11913      * Set this element to auto refresh.
11914      * @param {Number} interval How often to update (in seconds).
11915      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11916      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11918      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11919      */
11920     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11921         if(refreshNow){
11922             this.update(url || this.defaultUrl, params, callback, true);
11923         }
11924         if(this.autoRefreshProcId){
11925             clearInterval(this.autoRefreshProcId);
11926         }
11927         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11928     },
11929
11930     /**
11931      * Stop auto refresh on this element.
11932      */
11933      stopAutoRefresh : function(){
11934         if(this.autoRefreshProcId){
11935             clearInterval(this.autoRefreshProcId);
11936             delete this.autoRefreshProcId;
11937         }
11938     },
11939
11940     isAutoRefreshing : function(){
11941        return this.autoRefreshProcId ? true : false;
11942     },
11943     /**
11944      * Called to update the element to "Loading" state. Override to perform custom action.
11945      */
11946     showLoading : function(){
11947         if(this.showLoadIndicator){
11948             this.el.update(this.indicatorText);
11949         }
11950     },
11951
11952     /**
11953      * Adds unique parameter to query string if disableCaching = true
11954      * @private
11955      */
11956     prepareUrl : function(url){
11957         if(this.disableCaching){
11958             var append = "_dc=" + (new Date().getTime());
11959             if(url.indexOf("?") !== -1){
11960                 url += "&" + append;
11961             }else{
11962                 url += "?" + append;
11963             }
11964         }
11965         return url;
11966     },
11967
11968     /**
11969      * @private
11970      */
11971     processSuccess : function(response){
11972         this.transaction = null;
11973         if(response.argument.form && response.argument.reset){
11974             try{ // put in try/catch since some older FF releases had problems with this
11975                 response.argument.form.reset();
11976             }catch(e){}
11977         }
11978         if(this.loadScripts){
11979             this.renderer.render(this.el, response, this,
11980                 this.updateComplete.createDelegate(this, [response]));
11981         }else{
11982             this.renderer.render(this.el, response, this);
11983             this.updateComplete(response);
11984         }
11985     },
11986
11987     updateComplete : function(response){
11988         this.fireEvent("update", this.el, response);
11989         if(typeof response.argument.callback == "function"){
11990             response.argument.callback(this.el, true, response);
11991         }
11992     },
11993
11994     /**
11995      * @private
11996      */
11997     processFailure : function(response){
11998         this.transaction = null;
11999         this.fireEvent("failure", this.el, response);
12000         if(typeof response.argument.callback == "function"){
12001             response.argument.callback(this.el, false, response);
12002         }
12003     },
12004
12005     /**
12006      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12007      * @param {Object} renderer The object implementing the render() method
12008      */
12009     setRenderer : function(renderer){
12010         this.renderer = renderer;
12011     },
12012
12013     getRenderer : function(){
12014        return this.renderer;
12015     },
12016
12017     /**
12018      * Set the defaultUrl used for updates
12019      * @param {String/Function} defaultUrl The url or a function to call to get the url
12020      */
12021     setDefaultUrl : function(defaultUrl){
12022         this.defaultUrl = defaultUrl;
12023     },
12024
12025     /**
12026      * Aborts the executing transaction
12027      */
12028     abort : function(){
12029         if(this.transaction){
12030             Roo.Ajax.abort(this.transaction);
12031         }
12032     },
12033
12034     /**
12035      * Returns true if an update is in progress
12036      * @return {Boolean}
12037      */
12038     isUpdating : function(){
12039         if(this.transaction){
12040             return Roo.Ajax.isLoading(this.transaction);
12041         }
12042         return false;
12043     }
12044 });
12045
12046 /**
12047  * @class Roo.UpdateManager.defaults
12048  * @static (not really - but it helps the doc tool)
12049  * The defaults collection enables customizing the default properties of UpdateManager
12050  */
12051    Roo.UpdateManager.defaults = {
12052        /**
12053          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12054          * @type Number
12055          */
12056          timeout : 30,
12057
12058          /**
12059          * True to process scripts by default (Defaults to false).
12060          * @type Boolean
12061          */
12062         loadScripts : false,
12063
12064         /**
12065         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12066         * @type String
12067         */
12068         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12069         /**
12070          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12071          * @type Boolean
12072          */
12073         disableCaching : false,
12074         /**
12075          * Whether to show indicatorText when loading (Defaults to true).
12076          * @type Boolean
12077          */
12078         showLoadIndicator : true,
12079         /**
12080          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12081          * @type String
12082          */
12083         indicatorText : '<div class="loading-indicator">Loading...</div>'
12084    };
12085
12086 /**
12087  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12088  *Usage:
12089  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12090  * @param {String/HTMLElement/Roo.Element} el The element to update
12091  * @param {String} url The url
12092  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12093  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12094  * @static
12095  * @deprecated
12096  * @member Roo.UpdateManager
12097  */
12098 Roo.UpdateManager.updateElement = function(el, url, params, options){
12099     var um = Roo.get(el, true).getUpdateManager();
12100     Roo.apply(um, options);
12101     um.update(url, params, options ? options.callback : null);
12102 };
12103 // alias for backwards compat
12104 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12105 /**
12106  * @class Roo.UpdateManager.BasicRenderer
12107  * Default Content renderer. Updates the elements innerHTML with the responseText.
12108  */
12109 Roo.UpdateManager.BasicRenderer = function(){};
12110
12111 Roo.UpdateManager.BasicRenderer.prototype = {
12112     /**
12113      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12114      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12115      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12116      * @param {Roo.Element} el The element being rendered
12117      * @param {Object} response The YUI Connect response object
12118      * @param {UpdateManager} updateManager The calling update manager
12119      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12120      */
12121      render : function(el, response, updateManager, callback){
12122         el.update(response.responseText, updateManager.loadScripts, callback);
12123     }
12124 };
12125 /*
12126  * Based on:
12127  * Ext JS Library 1.1.1
12128  * Copyright(c) 2006-2007, Ext JS, LLC.
12129  *
12130  * Originally Released Under LGPL - original licence link has changed is not relivant.
12131  *
12132  * Fork - LGPL
12133  * <script type="text/javascript">
12134  */
12135
12136 /**
12137  * @class Roo.util.DelayedTask
12138  * Provides a convenient method of performing setTimeout where a new
12139  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12140  * You can use this class to buffer
12141  * the keypress events for a certain number of milliseconds, and perform only if they stop
12142  * for that amount of time.
12143  * @constructor The parameters to this constructor serve as defaults and are not required.
12144  * @param {Function} fn (optional) The default function to timeout
12145  * @param {Object} scope (optional) The default scope of that timeout
12146  * @param {Array} args (optional) The default Array of arguments
12147  */
12148 Roo.util.DelayedTask = function(fn, scope, args){
12149     var id = null, d, t;
12150
12151     var call = function(){
12152         var now = new Date().getTime();
12153         if(now - t >= d){
12154             clearInterval(id);
12155             id = null;
12156             fn.apply(scope, args || []);
12157         }
12158     };
12159     /**
12160      * Cancels any pending timeout and queues a new one
12161      * @param {Number} delay The milliseconds to delay
12162      * @param {Function} newFn (optional) Overrides function passed to constructor
12163      * @param {Object} newScope (optional) Overrides scope passed to constructor
12164      * @param {Array} newArgs (optional) Overrides args passed to constructor
12165      */
12166     this.delay = function(delay, newFn, newScope, newArgs){
12167         if(id && delay != d){
12168             this.cancel();
12169         }
12170         d = delay;
12171         t = new Date().getTime();
12172         fn = newFn || fn;
12173         scope = newScope || scope;
12174         args = newArgs || args;
12175         if(!id){
12176             id = setInterval(call, d);
12177         }
12178     };
12179
12180     /**
12181      * Cancel the last queued timeout
12182      */
12183     this.cancel = function(){
12184         if(id){
12185             clearInterval(id);
12186             id = null;
12187         }
12188     };
12189 };/*
12190  * Based on:
12191  * Ext JS Library 1.1.1
12192  * Copyright(c) 2006-2007, Ext JS, LLC.
12193  *
12194  * Originally Released Under LGPL - original licence link has changed is not relivant.
12195  *
12196  * Fork - LGPL
12197  * <script type="text/javascript">
12198  */
12199  
12200  
12201 Roo.util.TaskRunner = function(interval){
12202     interval = interval || 10;
12203     var tasks = [], removeQueue = [];
12204     var id = 0;
12205     var running = false;
12206
12207     var stopThread = function(){
12208         running = false;
12209         clearInterval(id);
12210         id = 0;
12211     };
12212
12213     var startThread = function(){
12214         if(!running){
12215             running = true;
12216             id = setInterval(runTasks, interval);
12217         }
12218     };
12219
12220     var removeTask = function(task){
12221         removeQueue.push(task);
12222         if(task.onStop){
12223             task.onStop();
12224         }
12225     };
12226
12227     var runTasks = function(){
12228         if(removeQueue.length > 0){
12229             for(var i = 0, len = removeQueue.length; i < len; i++){
12230                 tasks.remove(removeQueue[i]);
12231             }
12232             removeQueue = [];
12233             if(tasks.length < 1){
12234                 stopThread();
12235                 return;
12236             }
12237         }
12238         var now = new Date().getTime();
12239         for(var i = 0, len = tasks.length; i < len; ++i){
12240             var t = tasks[i];
12241             var itime = now - t.taskRunTime;
12242             if(t.interval <= itime){
12243                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12244                 t.taskRunTime = now;
12245                 if(rt === false || t.taskRunCount === t.repeat){
12246                     removeTask(t);
12247                     return;
12248                 }
12249             }
12250             if(t.duration && t.duration <= (now - t.taskStartTime)){
12251                 removeTask(t);
12252             }
12253         }
12254     };
12255
12256     /**
12257      * Queues a new task.
12258      * @param {Object} task
12259      */
12260     this.start = function(task){
12261         tasks.push(task);
12262         task.taskStartTime = new Date().getTime();
12263         task.taskRunTime = 0;
12264         task.taskRunCount = 0;
12265         startThread();
12266         return task;
12267     };
12268
12269     this.stop = function(task){
12270         removeTask(task);
12271         return task;
12272     };
12273
12274     this.stopAll = function(){
12275         stopThread();
12276         for(var i = 0, len = tasks.length; i < len; i++){
12277             if(tasks[i].onStop){
12278                 tasks[i].onStop();
12279             }
12280         }
12281         tasks = [];
12282         removeQueue = [];
12283     };
12284 };
12285
12286 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12287  * Based on:
12288  * Ext JS Library 1.1.1
12289  * Copyright(c) 2006-2007, Ext JS, LLC.
12290  *
12291  * Originally Released Under LGPL - original licence link has changed is not relivant.
12292  *
12293  * Fork - LGPL
12294  * <script type="text/javascript">
12295  */
12296
12297  
12298 /**
12299  * @class Roo.util.MixedCollection
12300  * @extends Roo.util.Observable
12301  * A Collection class that maintains both numeric indexes and keys and exposes events.
12302  * @constructor
12303  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12304  * collection (defaults to false)
12305  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12306  * and return the key value for that item.  This is used when available to look up the key on items that
12307  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12308  * equivalent to providing an implementation for the {@link #getKey} method.
12309  */
12310 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12311     this.items = [];
12312     this.map = {};
12313     this.keys = [];
12314     this.length = 0;
12315     this.addEvents({
12316         /**
12317          * @event clear
12318          * Fires when the collection is cleared.
12319          */
12320         "clear" : true,
12321         /**
12322          * @event add
12323          * Fires when an item is added to the collection.
12324          * @param {Number} index The index at which the item was added.
12325          * @param {Object} o The item added.
12326          * @param {String} key The key associated with the added item.
12327          */
12328         "add" : true,
12329         /**
12330          * @event replace
12331          * Fires when an item is replaced in the collection.
12332          * @param {String} key he key associated with the new added.
12333          * @param {Object} old The item being replaced.
12334          * @param {Object} new The new item.
12335          */
12336         "replace" : true,
12337         /**
12338          * @event remove
12339          * Fires when an item is removed from the collection.
12340          * @param {Object} o The item being removed.
12341          * @param {String} key (optional) The key associated with the removed item.
12342          */
12343         "remove" : true,
12344         "sort" : true
12345     });
12346     this.allowFunctions = allowFunctions === true;
12347     if(keyFn){
12348         this.getKey = keyFn;
12349     }
12350     Roo.util.MixedCollection.superclass.constructor.call(this);
12351 };
12352
12353 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12354     allowFunctions : false,
12355     
12356 /**
12357  * Adds an item to the collection.
12358  * @param {String} key The key to associate with the item
12359  * @param {Object} o The item to add.
12360  * @return {Object} The item added.
12361  */
12362     add : function(key, o){
12363         if(arguments.length == 1){
12364             o = arguments[0];
12365             key = this.getKey(o);
12366         }
12367         if(typeof key == "undefined" || key === null){
12368             this.length++;
12369             this.items.push(o);
12370             this.keys.push(null);
12371         }else{
12372             var old = this.map[key];
12373             if(old){
12374                 return this.replace(key, o);
12375             }
12376             this.length++;
12377             this.items.push(o);
12378             this.map[key] = o;
12379             this.keys.push(key);
12380         }
12381         this.fireEvent("add", this.length-1, o, key);
12382         return o;
12383     },
12384        
12385 /**
12386   * MixedCollection has a generic way to fetch keys if you implement getKey.
12387 <pre><code>
12388 // normal way
12389 var mc = new Roo.util.MixedCollection();
12390 mc.add(someEl.dom.id, someEl);
12391 mc.add(otherEl.dom.id, otherEl);
12392 //and so on
12393
12394 // using getKey
12395 var mc = new Roo.util.MixedCollection();
12396 mc.getKey = function(el){
12397    return el.dom.id;
12398 };
12399 mc.add(someEl);
12400 mc.add(otherEl);
12401
12402 // or via the constructor
12403 var mc = new Roo.util.MixedCollection(false, function(el){
12404    return el.dom.id;
12405 });
12406 mc.add(someEl);
12407 mc.add(otherEl);
12408 </code></pre>
12409  * @param o {Object} The item for which to find the key.
12410  * @return {Object} The key for the passed item.
12411  */
12412     getKey : function(o){
12413          return o.id; 
12414     },
12415    
12416 /**
12417  * Replaces an item in the collection.
12418  * @param {String} key The key associated with the item to replace, or the item to replace.
12419  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12420  * @return {Object}  The new item.
12421  */
12422     replace : function(key, o){
12423         if(arguments.length == 1){
12424             o = arguments[0];
12425             key = this.getKey(o);
12426         }
12427         var old = this.item(key);
12428         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12429              return this.add(key, o);
12430         }
12431         var index = this.indexOfKey(key);
12432         this.items[index] = o;
12433         this.map[key] = o;
12434         this.fireEvent("replace", key, old, o);
12435         return o;
12436     },
12437    
12438 /**
12439  * Adds all elements of an Array or an Object to the collection.
12440  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12441  * an Array of values, each of which are added to the collection.
12442  */
12443     addAll : function(objs){
12444         if(arguments.length > 1 || objs instanceof Array){
12445             var args = arguments.length > 1 ? arguments : objs;
12446             for(var i = 0, len = args.length; i < len; i++){
12447                 this.add(args[i]);
12448             }
12449         }else{
12450             for(var key in objs){
12451                 if(this.allowFunctions || typeof objs[key] != "function"){
12452                     this.add(key, objs[key]);
12453                 }
12454             }
12455         }
12456     },
12457    
12458 /**
12459  * Executes the specified function once for every item in the collection, passing each
12460  * item as the first and only parameter. returning false from the function will stop the iteration.
12461  * @param {Function} fn The function to execute for each item.
12462  * @param {Object} scope (optional) The scope in which to execute the function.
12463  */
12464     each : function(fn, scope){
12465         var items = [].concat(this.items); // each safe for removal
12466         for(var i = 0, len = items.length; i < len; i++){
12467             if(fn.call(scope || items[i], items[i], i, len) === false){
12468                 break;
12469             }
12470         }
12471     },
12472    
12473 /**
12474  * Executes the specified function once for every key in the collection, passing each
12475  * key, and its associated item as the first two parameters.
12476  * @param {Function} fn The function to execute for each item.
12477  * @param {Object} scope (optional) The scope in which to execute the function.
12478  */
12479     eachKey : function(fn, scope){
12480         for(var i = 0, len = this.keys.length; i < len; i++){
12481             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12482         }
12483     },
12484    
12485 /**
12486  * Returns the first item in the collection which elicits a true return value from the
12487  * passed selection function.
12488  * @param {Function} fn The selection function to execute for each item.
12489  * @param {Object} scope (optional) The scope in which to execute the function.
12490  * @return {Object} The first item in the collection which returned true from the selection function.
12491  */
12492     find : function(fn, scope){
12493         for(var i = 0, len = this.items.length; i < len; i++){
12494             if(fn.call(scope || window, this.items[i], this.keys[i])){
12495                 return this.items[i];
12496             }
12497         }
12498         return null;
12499     },
12500    
12501 /**
12502  * Inserts an item at the specified index in the collection.
12503  * @param {Number} index The index to insert the item at.
12504  * @param {String} key The key to associate with the new item, or the item itself.
12505  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12506  * @return {Object} The item inserted.
12507  */
12508     insert : function(index, key, o){
12509         if(arguments.length == 2){
12510             o = arguments[1];
12511             key = this.getKey(o);
12512         }
12513         if(index >= this.length){
12514             return this.add(key, o);
12515         }
12516         this.length++;
12517         this.items.splice(index, 0, o);
12518         if(typeof key != "undefined" && key != null){
12519             this.map[key] = o;
12520         }
12521         this.keys.splice(index, 0, key);
12522         this.fireEvent("add", index, o, key);
12523         return o;
12524     },
12525    
12526 /**
12527  * Removed an item from the collection.
12528  * @param {Object} o The item to remove.
12529  * @return {Object} The item removed.
12530  */
12531     remove : function(o){
12532         return this.removeAt(this.indexOf(o));
12533     },
12534    
12535 /**
12536  * Remove an item from a specified index in the collection.
12537  * @param {Number} index The index within the collection of the item to remove.
12538  */
12539     removeAt : function(index){
12540         if(index < this.length && index >= 0){
12541             this.length--;
12542             var o = this.items[index];
12543             this.items.splice(index, 1);
12544             var key = this.keys[index];
12545             if(typeof key != "undefined"){
12546                 delete this.map[key];
12547             }
12548             this.keys.splice(index, 1);
12549             this.fireEvent("remove", o, key);
12550         }
12551     },
12552    
12553 /**
12554  * Removed an item associated with the passed key fom the collection.
12555  * @param {String} key The key of the item to remove.
12556  */
12557     removeKey : function(key){
12558         return this.removeAt(this.indexOfKey(key));
12559     },
12560    
12561 /**
12562  * Returns the number of items in the collection.
12563  * @return {Number} the number of items in the collection.
12564  */
12565     getCount : function(){
12566         return this.length; 
12567     },
12568    
12569 /**
12570  * Returns index within the collection of the passed Object.
12571  * @param {Object} o The item to find the index of.
12572  * @return {Number} index of the item.
12573  */
12574     indexOf : function(o){
12575         if(!this.items.indexOf){
12576             for(var i = 0, len = this.items.length; i < len; i++){
12577                 if(this.items[i] == o) return i;
12578             }
12579             return -1;
12580         }else{
12581             return this.items.indexOf(o);
12582         }
12583     },
12584    
12585 /**
12586  * Returns index within the collection of the passed key.
12587  * @param {String} key The key to find the index of.
12588  * @return {Number} index of the key.
12589  */
12590     indexOfKey : function(key){
12591         if(!this.keys.indexOf){
12592             for(var i = 0, len = this.keys.length; i < len; i++){
12593                 if(this.keys[i] == key) return i;
12594             }
12595             return -1;
12596         }else{
12597             return this.keys.indexOf(key);
12598         }
12599     },
12600    
12601 /**
12602  * Returns the item associated with the passed key OR index. Key has priority over index.
12603  * @param {String/Number} key The key or index of the item.
12604  * @return {Object} The item associated with the passed key.
12605  */
12606     item : function(key){
12607         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12608         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12609     },
12610     
12611 /**
12612  * Returns the item at the specified index.
12613  * @param {Number} index The index of the item.
12614  * @return {Object}
12615  */
12616     itemAt : function(index){
12617         return this.items[index];
12618     },
12619     
12620 /**
12621  * Returns the item associated with the passed key.
12622  * @param {String/Number} key The key of the item.
12623  * @return {Object} The item associated with the passed key.
12624  */
12625     key : function(key){
12626         return this.map[key];
12627     },
12628    
12629 /**
12630  * Returns true if the collection contains the passed Object as an item.
12631  * @param {Object} o  The Object to look for in the collection.
12632  * @return {Boolean} True if the collection contains the Object as an item.
12633  */
12634     contains : function(o){
12635         return this.indexOf(o) != -1;
12636     },
12637    
12638 /**
12639  * Returns true if the collection contains the passed Object as a key.
12640  * @param {String} key The key to look for in the collection.
12641  * @return {Boolean} True if the collection contains the Object as a key.
12642  */
12643     containsKey : function(key){
12644         return typeof this.map[key] != "undefined";
12645     },
12646    
12647 /**
12648  * Removes all items from the collection.
12649  */
12650     clear : function(){
12651         this.length = 0;
12652         this.items = [];
12653         this.keys = [];
12654         this.map = {};
12655         this.fireEvent("clear");
12656     },
12657    
12658 /**
12659  * Returns the first item in the collection.
12660  * @return {Object} the first item in the collection..
12661  */
12662     first : function(){
12663         return this.items[0]; 
12664     },
12665    
12666 /**
12667  * Returns the last item in the collection.
12668  * @return {Object} the last item in the collection..
12669  */
12670     last : function(){
12671         return this.items[this.length-1];   
12672     },
12673     
12674     _sort : function(property, dir, fn){
12675         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12676         fn = fn || function(a, b){
12677             return a-b;
12678         };
12679         var c = [], k = this.keys, items = this.items;
12680         for(var i = 0, len = items.length; i < len; i++){
12681             c[c.length] = {key: k[i], value: items[i], index: i};
12682         }
12683         c.sort(function(a, b){
12684             var v = fn(a[property], b[property]) * dsc;
12685             if(v == 0){
12686                 v = (a.index < b.index ? -1 : 1);
12687             }
12688             return v;
12689         });
12690         for(var i = 0, len = c.length; i < len; i++){
12691             items[i] = c[i].value;
12692             k[i] = c[i].key;
12693         }
12694         this.fireEvent("sort", this);
12695     },
12696     
12697     /**
12698      * Sorts this collection with the passed comparison function
12699      * @param {String} direction (optional) "ASC" or "DESC"
12700      * @param {Function} fn (optional) comparison function
12701      */
12702     sort : function(dir, fn){
12703         this._sort("value", dir, fn);
12704     },
12705     
12706     /**
12707      * Sorts this collection by keys
12708      * @param {String} direction (optional) "ASC" or "DESC"
12709      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12710      */
12711     keySort : function(dir, fn){
12712         this._sort("key", dir, fn || function(a, b){
12713             return String(a).toUpperCase()-String(b).toUpperCase();
12714         });
12715     },
12716     
12717     /**
12718      * Returns a range of items in this collection
12719      * @param {Number} startIndex (optional) defaults to 0
12720      * @param {Number} endIndex (optional) default to the last item
12721      * @return {Array} An array of items
12722      */
12723     getRange : function(start, end){
12724         var items = this.items;
12725         if(items.length < 1){
12726             return [];
12727         }
12728         start = start || 0;
12729         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12730         var r = [];
12731         if(start <= end){
12732             for(var i = start; i <= end; i++) {
12733                     r[r.length] = items[i];
12734             }
12735         }else{
12736             for(var i = start; i >= end; i--) {
12737                     r[r.length] = items[i];
12738             }
12739         }
12740         return r;
12741     },
12742         
12743     /**
12744      * Filter the <i>objects</i> in this collection by a specific property. 
12745      * Returns a new collection that has been filtered.
12746      * @param {String} property A property on your objects
12747      * @param {String/RegExp} value Either string that the property values 
12748      * should start with or a RegExp to test against the property
12749      * @return {MixedCollection} The new filtered collection
12750      */
12751     filter : function(property, value){
12752         if(!value.exec){ // not a regex
12753             value = String(value);
12754             if(value.length == 0){
12755                 return this.clone();
12756             }
12757             value = new RegExp("^" + Roo.escapeRe(value), "i");
12758         }
12759         return this.filterBy(function(o){
12760             return o && value.test(o[property]);
12761         });
12762         },
12763     
12764     /**
12765      * Filter by a function. * Returns a new collection that has been filtered.
12766      * The passed function will be called with each 
12767      * object in the collection. If the function returns true, the value is included 
12768      * otherwise it is filtered.
12769      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12770      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12771      * @return {MixedCollection} The new filtered collection
12772      */
12773     filterBy : function(fn, scope){
12774         var r = new Roo.util.MixedCollection();
12775         r.getKey = this.getKey;
12776         var k = this.keys, it = this.items;
12777         for(var i = 0, len = it.length; i < len; i++){
12778             if(fn.call(scope||this, it[i], k[i])){
12779                                 r.add(k[i], it[i]);
12780                         }
12781         }
12782         return r;
12783     },
12784     
12785     /**
12786      * Creates a duplicate of this collection
12787      * @return {MixedCollection}
12788      */
12789     clone : function(){
12790         var r = new Roo.util.MixedCollection();
12791         var k = this.keys, it = this.items;
12792         for(var i = 0, len = it.length; i < len; i++){
12793             r.add(k[i], it[i]);
12794         }
12795         r.getKey = this.getKey;
12796         return r;
12797     }
12798 });
12799 /**
12800  * Returns the item associated with the passed key or index.
12801  * @method
12802  * @param {String/Number} key The key or index of the item.
12803  * @return {Object} The item associated with the passed key.
12804  */
12805 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12806  * Based on:
12807  * Ext JS Library 1.1.1
12808  * Copyright(c) 2006-2007, Ext JS, LLC.
12809  *
12810  * Originally Released Under LGPL - original licence link has changed is not relivant.
12811  *
12812  * Fork - LGPL
12813  * <script type="text/javascript">
12814  */
12815 /**
12816  * @class Roo.util.JSON
12817  * Modified version of Douglas Crockford"s json.js that doesn"t
12818  * mess with the Object prototype 
12819  * http://www.json.org/js.html
12820  * @singleton
12821  */
12822 Roo.util.JSON = new (function(){
12823     var useHasOwn = {}.hasOwnProperty ? true : false;
12824     
12825     // crashes Safari in some instances
12826     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12827     
12828     var pad = function(n) {
12829         return n < 10 ? "0" + n : n;
12830     };
12831     
12832     var m = {
12833         "\b": '\\b',
12834         "\t": '\\t',
12835         "\n": '\\n',
12836         "\f": '\\f',
12837         "\r": '\\r',
12838         '"' : '\\"',
12839         "\\": '\\\\'
12840     };
12841
12842     var encodeString = function(s){
12843         if (/["\\\x00-\x1f]/.test(s)) {
12844             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12845                 var c = m[b];
12846                 if(c){
12847                     return c;
12848                 }
12849                 c = b.charCodeAt();
12850                 return "\\u00" +
12851                     Math.floor(c / 16).toString(16) +
12852                     (c % 16).toString(16);
12853             }) + '"';
12854         }
12855         return '"' + s + '"';
12856     };
12857     
12858     var encodeArray = function(o){
12859         var a = ["["], b, i, l = o.length, v;
12860             for (i = 0; i < l; i += 1) {
12861                 v = o[i];
12862                 switch (typeof v) {
12863                     case "undefined":
12864                     case "function":
12865                     case "unknown":
12866                         break;
12867                     default:
12868                         if (b) {
12869                             a.push(',');
12870                         }
12871                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12872                         b = true;
12873                 }
12874             }
12875             a.push("]");
12876             return a.join("");
12877     };
12878     
12879     var encodeDate = function(o){
12880         return '"' + o.getFullYear() + "-" +
12881                 pad(o.getMonth() + 1) + "-" +
12882                 pad(o.getDate()) + "T" +
12883                 pad(o.getHours()) + ":" +
12884                 pad(o.getMinutes()) + ":" +
12885                 pad(o.getSeconds()) + '"';
12886     };
12887     
12888     /**
12889      * Encodes an Object, Array or other value
12890      * @param {Mixed} o The variable to encode
12891      * @return {String} The JSON string
12892      */
12893     this.encode = function(o)
12894     {
12895         // should this be extended to fully wrap stringify..
12896         
12897         if(typeof o == "undefined" || o === null){
12898             return "null";
12899         }else if(o instanceof Array){
12900             return encodeArray(o);
12901         }else if(o instanceof Date){
12902             return encodeDate(o);
12903         }else if(typeof o == "string"){
12904             return encodeString(o);
12905         }else if(typeof o == "number"){
12906             return isFinite(o) ? String(o) : "null";
12907         }else if(typeof o == "boolean"){
12908             return String(o);
12909         }else {
12910             var a = ["{"], b, i, v;
12911             for (i in o) {
12912                 if(!useHasOwn || o.hasOwnProperty(i)) {
12913                     v = o[i];
12914                     switch (typeof v) {
12915                     case "undefined":
12916                     case "function":
12917                     case "unknown":
12918                         break;
12919                     default:
12920                         if(b){
12921                             a.push(',');
12922                         }
12923                         a.push(this.encode(i), ":",
12924                                 v === null ? "null" : this.encode(v));
12925                         b = true;
12926                     }
12927                 }
12928             }
12929             a.push("}");
12930             return a.join("");
12931         }
12932     };
12933     
12934     /**
12935      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12936      * @param {String} json The JSON string
12937      * @return {Object} The resulting object
12938      */
12939     this.decode = function(json){
12940         
12941         return  /** eval:var:json */ eval("(" + json + ')');
12942     };
12943 })();
12944 /** 
12945  * Shorthand for {@link Roo.util.JSON#encode}
12946  * @member Roo encode 
12947  * @method */
12948 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12949 /** 
12950  * Shorthand for {@link Roo.util.JSON#decode}
12951  * @member Roo decode 
12952  * @method */
12953 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12954 /*
12955  * Based on:
12956  * Ext JS Library 1.1.1
12957  * Copyright(c) 2006-2007, Ext JS, LLC.
12958  *
12959  * Originally Released Under LGPL - original licence link has changed is not relivant.
12960  *
12961  * Fork - LGPL
12962  * <script type="text/javascript">
12963  */
12964  
12965 /**
12966  * @class Roo.util.Format
12967  * Reusable data formatting functions
12968  * @singleton
12969  */
12970 Roo.util.Format = function(){
12971     var trimRe = /^\s+|\s+$/g;
12972     return {
12973         /**
12974          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12975          * @param {String} value The string to truncate
12976          * @param {Number} length The maximum length to allow before truncating
12977          * @return {String} The converted text
12978          */
12979         ellipsis : function(value, len){
12980             if(value && value.length > len){
12981                 return value.substr(0, len-3)+"...";
12982             }
12983             return value;
12984         },
12985
12986         /**
12987          * Checks a reference and converts it to empty string if it is undefined
12988          * @param {Mixed} value Reference to check
12989          * @return {Mixed} Empty string if converted, otherwise the original value
12990          */
12991         undef : function(value){
12992             return typeof value != "undefined" ? value : "";
12993         },
12994
12995         /**
12996          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12997          * @param {String} value The string to encode
12998          * @return {String} The encoded text
12999          */
13000         htmlEncode : function(value){
13001             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13002         },
13003
13004         /**
13005          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13006          * @param {String} value The string to decode
13007          * @return {String} The decoded text
13008          */
13009         htmlDecode : function(value){
13010             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13011         },
13012
13013         /**
13014          * Trims any whitespace from either side of a string
13015          * @param {String} value The text to trim
13016          * @return {String} The trimmed text
13017          */
13018         trim : function(value){
13019             return String(value).replace(trimRe, "");
13020         },
13021
13022         /**
13023          * Returns a substring from within an original string
13024          * @param {String} value The original text
13025          * @param {Number} start The start index of the substring
13026          * @param {Number} length The length of the substring
13027          * @return {String} The substring
13028          */
13029         substr : function(value, start, length){
13030             return String(value).substr(start, length);
13031         },
13032
13033         /**
13034          * Converts a string to all lower case letters
13035          * @param {String} value The text to convert
13036          * @return {String} The converted text
13037          */
13038         lowercase : function(value){
13039             return String(value).toLowerCase();
13040         },
13041
13042         /**
13043          * Converts a string to all upper case letters
13044          * @param {String} value The text to convert
13045          * @return {String} The converted text
13046          */
13047         uppercase : function(value){
13048             return String(value).toUpperCase();
13049         },
13050
13051         /**
13052          * Converts the first character only of a string to upper case
13053          * @param {String} value The text to convert
13054          * @return {String} The converted text
13055          */
13056         capitalize : function(value){
13057             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13058         },
13059
13060         // private
13061         call : function(value, fn){
13062             if(arguments.length > 2){
13063                 var args = Array.prototype.slice.call(arguments, 2);
13064                 args.unshift(value);
13065                  
13066                 return /** eval:var:value */  eval(fn).apply(window, args);
13067             }else{
13068                 /** eval:var:value */
13069                 return /** eval:var:value */ eval(fn).call(window, value);
13070             }
13071         },
13072
13073        
13074         /**
13075          * safer version of Math.toFixed..??/
13076          * @param {Number/String} value The numeric value to format
13077          * @param {Number/String} value Decimal places 
13078          * @return {String} The formatted currency string
13079          */
13080         toFixed : function(v, n)
13081         {
13082             // why not use to fixed - precision is buggered???
13083             if (!n) {
13084                 return Math.round(v-0);
13085             }
13086             var fact = Math.pow(10,n+1);
13087             v = (Math.round((v-0)*fact))/fact;
13088             var z = (''+fact).substring(2);
13089             if (v == Math.floor(v)) {
13090                 return Math.floor(v) + '.' + z;
13091             }
13092             
13093             // now just padd decimals..
13094             var ps = String(v).split('.');
13095             var fd = (ps[1] + z);
13096             var r = fd.substring(0,n); 
13097             var rm = fd.substring(n); 
13098             if (rm < 5) {
13099                 return ps[0] + '.' + r;
13100             }
13101             r*=1; // turn it into a number;
13102             r++;
13103             if (String(r).length != n) {
13104                 ps[0]*=1;
13105                 ps[0]++;
13106                 r = String(r).substring(1); // chop the end off.
13107             }
13108             
13109             return ps[0] + '.' + r;
13110              
13111         },
13112         
13113         /**
13114          * Format a number as US currency
13115          * @param {Number/String} value The numeric value to format
13116          * @return {String} The formatted currency string
13117          */
13118         usMoney : function(v){
13119             v = (Math.round((v-0)*100))/100;
13120             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13121             v = String(v);
13122             var ps = v.split('.');
13123             var whole = ps[0];
13124             var sub = ps[1] ? '.'+ ps[1] : '.00';
13125             var r = /(\d+)(\d{3})/;
13126             while (r.test(whole)) {
13127                 whole = whole.replace(r, '$1' + ',' + '$2');
13128             }
13129             return "$" + whole + sub ;
13130         },
13131         
13132         /**
13133          * Parse a value into a formatted date using the specified format pattern.
13134          * @param {Mixed} value The value to format
13135          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13136          * @return {String} The formatted date string
13137          */
13138         date : function(v, format){
13139             if(!v){
13140                 return "";
13141             }
13142             if(!(v instanceof Date)){
13143                 v = new Date(Date.parse(v));
13144             }
13145             return v.dateFormat(format || "m/d/Y");
13146         },
13147
13148         /**
13149          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13150          * @param {String} format Any valid date format string
13151          * @return {Function} The date formatting function
13152          */
13153         dateRenderer : function(format){
13154             return function(v){
13155                 return Roo.util.Format.date(v, format);  
13156             };
13157         },
13158
13159         // private
13160         stripTagsRE : /<\/?[^>]+>/gi,
13161         
13162         /**
13163          * Strips all HTML tags
13164          * @param {Mixed} value The text from which to strip tags
13165          * @return {String} The stripped text
13166          */
13167         stripTags : function(v){
13168             return !v ? v : String(v).replace(this.stripTagsRE, "");
13169         }
13170     };
13171 }();/*
13172  * Based on:
13173  * Ext JS Library 1.1.1
13174  * Copyright(c) 2006-2007, Ext JS, LLC.
13175  *
13176  * Originally Released Under LGPL - original licence link has changed is not relivant.
13177  *
13178  * Fork - LGPL
13179  * <script type="text/javascript">
13180  */
13181
13182
13183  
13184
13185 /**
13186  * @class Roo.MasterTemplate
13187  * @extends Roo.Template
13188  * Provides a template that can have child templates. The syntax is:
13189 <pre><code>
13190 var t = new Roo.MasterTemplate(
13191         '&lt;select name="{name}"&gt;',
13192                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13193         '&lt;/select&gt;'
13194 );
13195 t.add('options', {value: 'foo', text: 'bar'});
13196 // or you can add multiple child elements in one shot
13197 t.addAll('options', [
13198     {value: 'foo', text: 'bar'},
13199     {value: 'foo2', text: 'bar2'},
13200     {value: 'foo3', text: 'bar3'}
13201 ]);
13202 // then append, applying the master template values
13203 t.append('my-form', {name: 'my-select'});
13204 </code></pre>
13205 * A name attribute for the child template is not required if you have only one child
13206 * template or you want to refer to them by index.
13207  */
13208 Roo.MasterTemplate = function(){
13209     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13210     this.originalHtml = this.html;
13211     var st = {};
13212     var m, re = this.subTemplateRe;
13213     re.lastIndex = 0;
13214     var subIndex = 0;
13215     while(m = re.exec(this.html)){
13216         var name = m[1], content = m[2];
13217         st[subIndex] = {
13218             name: name,
13219             index: subIndex,
13220             buffer: [],
13221             tpl : new Roo.Template(content)
13222         };
13223         if(name){
13224             st[name] = st[subIndex];
13225         }
13226         st[subIndex].tpl.compile();
13227         st[subIndex].tpl.call = this.call.createDelegate(this);
13228         subIndex++;
13229     }
13230     this.subCount = subIndex;
13231     this.subs = st;
13232 };
13233 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13234     /**
13235     * The regular expression used to match sub templates
13236     * @type RegExp
13237     * @property
13238     */
13239     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13240
13241     /**
13242      * Applies the passed values to a child template.
13243      * @param {String/Number} name (optional) The name or index of the child template
13244      * @param {Array/Object} values The values to be applied to the template
13245      * @return {MasterTemplate} this
13246      */
13247      add : function(name, values){
13248         if(arguments.length == 1){
13249             values = arguments[0];
13250             name = 0;
13251         }
13252         var s = this.subs[name];
13253         s.buffer[s.buffer.length] = s.tpl.apply(values);
13254         return this;
13255     },
13256
13257     /**
13258      * Applies all the passed values to a child template.
13259      * @param {String/Number} name (optional) The name or index of the child template
13260      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13261      * @param {Boolean} reset (optional) True to reset the template first
13262      * @return {MasterTemplate} this
13263      */
13264     fill : function(name, values, reset){
13265         var a = arguments;
13266         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13267             values = a[0];
13268             name = 0;
13269             reset = a[1];
13270         }
13271         if(reset){
13272             this.reset();
13273         }
13274         for(var i = 0, len = values.length; i < len; i++){
13275             this.add(name, values[i]);
13276         }
13277         return this;
13278     },
13279
13280     /**
13281      * Resets the template for reuse
13282      * @return {MasterTemplate} this
13283      */
13284      reset : function(){
13285         var s = this.subs;
13286         for(var i = 0; i < this.subCount; i++){
13287             s[i].buffer = [];
13288         }
13289         return this;
13290     },
13291
13292     applyTemplate : function(values){
13293         var s = this.subs;
13294         var replaceIndex = -1;
13295         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13296             return s[++replaceIndex].buffer.join("");
13297         });
13298         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13299     },
13300
13301     apply : function(){
13302         return this.applyTemplate.apply(this, arguments);
13303     },
13304
13305     compile : function(){return this;}
13306 });
13307
13308 /**
13309  * Alias for fill().
13310  * @method
13311  */
13312 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13313  /**
13314  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13315  * var tpl = Roo.MasterTemplate.from('element-id');
13316  * @param {String/HTMLElement} el
13317  * @param {Object} config
13318  * @static
13319  */
13320 Roo.MasterTemplate.from = function(el, config){
13321     el = Roo.getDom(el);
13322     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13323 };/*
13324  * Based on:
13325  * Ext JS Library 1.1.1
13326  * Copyright(c) 2006-2007, Ext JS, LLC.
13327  *
13328  * Originally Released Under LGPL - original licence link has changed is not relivant.
13329  *
13330  * Fork - LGPL
13331  * <script type="text/javascript">
13332  */
13333
13334  
13335 /**
13336  * @class Roo.util.CSS
13337  * Utility class for manipulating CSS rules
13338  * @singleton
13339  */
13340 Roo.util.CSS = function(){
13341         var rules = null;
13342         var doc = document;
13343
13344     var camelRe = /(-[a-z])/gi;
13345     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13346
13347    return {
13348    /**
13349     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13350     * tag and appended to the HEAD of the document.
13351     * @param {String|Object} cssText The text containing the css rules
13352     * @param {String} id An id to add to the stylesheet for later removal
13353     * @return {StyleSheet}
13354     */
13355     createStyleSheet : function(cssText, id){
13356         var ss;
13357         var head = doc.getElementsByTagName("head")[0];
13358         var nrules = doc.createElement("style");
13359         nrules.setAttribute("type", "text/css");
13360         if(id){
13361             nrules.setAttribute("id", id);
13362         }
13363         if (typeof(cssText) != 'string') {
13364             // support object maps..
13365             // not sure if this a good idea.. 
13366             // perhaps it should be merged with the general css handling
13367             // and handle js style props.
13368             var cssTextNew = [];
13369             for(var n in cssText) {
13370                 var citems = [];
13371                 for(var k in cssText[n]) {
13372                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13373                 }
13374                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13375                 
13376             }
13377             cssText = cssTextNew.join("\n");
13378             
13379         }
13380        
13381        
13382        if(Roo.isIE){
13383            head.appendChild(nrules);
13384            ss = nrules.styleSheet;
13385            ss.cssText = cssText;
13386        }else{
13387            try{
13388                 nrules.appendChild(doc.createTextNode(cssText));
13389            }catch(e){
13390                nrules.cssText = cssText; 
13391            }
13392            head.appendChild(nrules);
13393            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13394        }
13395        this.cacheStyleSheet(ss);
13396        return ss;
13397    },
13398
13399    /**
13400     * Removes a style or link tag by id
13401     * @param {String} id The id of the tag
13402     */
13403    removeStyleSheet : function(id){
13404        var existing = doc.getElementById(id);
13405        if(existing){
13406            existing.parentNode.removeChild(existing);
13407        }
13408    },
13409
13410    /**
13411     * Dynamically swaps an existing stylesheet reference for a new one
13412     * @param {String} id The id of an existing link tag to remove
13413     * @param {String} url The href of the new stylesheet to include
13414     */
13415    swapStyleSheet : function(id, url){
13416        this.removeStyleSheet(id);
13417        var ss = doc.createElement("link");
13418        ss.setAttribute("rel", "stylesheet");
13419        ss.setAttribute("type", "text/css");
13420        ss.setAttribute("id", id);
13421        ss.setAttribute("href", url);
13422        doc.getElementsByTagName("head")[0].appendChild(ss);
13423    },
13424    
13425    /**
13426     * Refresh the rule cache if you have dynamically added stylesheets
13427     * @return {Object} An object (hash) of rules indexed by selector
13428     */
13429    refreshCache : function(){
13430        return this.getRules(true);
13431    },
13432
13433    // private
13434    cacheStyleSheet : function(stylesheet){
13435        if(!rules){
13436            rules = {};
13437        }
13438        try{// try catch for cross domain access issue
13439            var ssRules = stylesheet.cssRules || stylesheet.rules;
13440            for(var j = ssRules.length-1; j >= 0; --j){
13441                rules[ssRules[j].selectorText] = ssRules[j];
13442            }
13443        }catch(e){}
13444    },
13445    
13446    /**
13447     * Gets all css rules for the document
13448     * @param {Boolean} refreshCache true to refresh the internal cache
13449     * @return {Object} An object (hash) of rules indexed by selector
13450     */
13451    getRules : function(refreshCache){
13452                 if(rules == null || refreshCache){
13453                         rules = {};
13454                         var ds = doc.styleSheets;
13455                         for(var i =0, len = ds.length; i < len; i++){
13456                             try{
13457                         this.cacheStyleSheet(ds[i]);
13458                     }catch(e){} 
13459                 }
13460                 }
13461                 return rules;
13462         },
13463         
13464         /**
13465     * Gets an an individual CSS rule by selector(s)
13466     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13467     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13468     * @return {CSSRule} The CSS rule or null if one is not found
13469     */
13470    getRule : function(selector, refreshCache){
13471                 var rs = this.getRules(refreshCache);
13472                 if(!(selector instanceof Array)){
13473                     return rs[selector];
13474                 }
13475                 for(var i = 0; i < selector.length; i++){
13476                         if(rs[selector[i]]){
13477                                 return rs[selector[i]];
13478                         }
13479                 }
13480                 return null;
13481         },
13482         
13483         
13484         /**
13485     * Updates a rule property
13486     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13487     * @param {String} property The css property
13488     * @param {String} value The new value for the property
13489     * @return {Boolean} true If a rule was found and updated
13490     */
13491    updateRule : function(selector, property, value){
13492                 if(!(selector instanceof Array)){
13493                         var rule = this.getRule(selector);
13494                         if(rule){
13495                                 rule.style[property.replace(camelRe, camelFn)] = value;
13496                                 return true;
13497                         }
13498                 }else{
13499                         for(var i = 0; i < selector.length; i++){
13500                                 if(this.updateRule(selector[i], property, value)){
13501                                         return true;
13502                                 }
13503                         }
13504                 }
13505                 return false;
13506         }
13507    };   
13508 }();/*
13509  * Based on:
13510  * Ext JS Library 1.1.1
13511  * Copyright(c) 2006-2007, Ext JS, LLC.
13512  *
13513  * Originally Released Under LGPL - original licence link has changed is not relivant.
13514  *
13515  * Fork - LGPL
13516  * <script type="text/javascript">
13517  */
13518
13519  
13520
13521 /**
13522  * @class Roo.util.ClickRepeater
13523  * @extends Roo.util.Observable
13524  * 
13525  * A wrapper class which can be applied to any element. Fires a "click" event while the
13526  * mouse is pressed. The interval between firings may be specified in the config but
13527  * defaults to 10 milliseconds.
13528  * 
13529  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13530  * 
13531  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13532  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13533  * Similar to an autorepeat key delay.
13534  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13535  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13536  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13537  *           "interval" and "delay" are ignored. "immediate" is honored.
13538  * @cfg {Boolean} preventDefault True to prevent the default click event
13539  * @cfg {Boolean} stopDefault True to stop the default click event
13540  * 
13541  * @history
13542  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13543  *     2007-02-02 jvs Renamed to ClickRepeater
13544  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13545  *
13546  *  @constructor
13547  * @param {String/HTMLElement/Element} el The element to listen on
13548  * @param {Object} config
13549  **/
13550 Roo.util.ClickRepeater = function(el, config)
13551 {
13552     this.el = Roo.get(el);
13553     this.el.unselectable();
13554
13555     Roo.apply(this, config);
13556
13557     this.addEvents({
13558     /**
13559      * @event mousedown
13560      * Fires when the mouse button is depressed.
13561      * @param {Roo.util.ClickRepeater} this
13562      */
13563         "mousedown" : true,
13564     /**
13565      * @event click
13566      * Fires on a specified interval during the time the element is pressed.
13567      * @param {Roo.util.ClickRepeater} this
13568      */
13569         "click" : true,
13570     /**
13571      * @event mouseup
13572      * Fires when the mouse key is released.
13573      * @param {Roo.util.ClickRepeater} this
13574      */
13575         "mouseup" : true
13576     });
13577
13578     this.el.on("mousedown", this.handleMouseDown, this);
13579     if(this.preventDefault || this.stopDefault){
13580         this.el.on("click", function(e){
13581             if(this.preventDefault){
13582                 e.preventDefault();
13583             }
13584             if(this.stopDefault){
13585                 e.stopEvent();
13586             }
13587         }, this);
13588     }
13589
13590     // allow inline handler
13591     if(this.handler){
13592         this.on("click", this.handler,  this.scope || this);
13593     }
13594
13595     Roo.util.ClickRepeater.superclass.constructor.call(this);
13596 };
13597
13598 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13599     interval : 20,
13600     delay: 250,
13601     preventDefault : true,
13602     stopDefault : false,
13603     timer : 0,
13604
13605     // private
13606     handleMouseDown : function(){
13607         clearTimeout(this.timer);
13608         this.el.blur();
13609         if(this.pressClass){
13610             this.el.addClass(this.pressClass);
13611         }
13612         this.mousedownTime = new Date();
13613
13614         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13615         this.el.on("mouseout", this.handleMouseOut, this);
13616
13617         this.fireEvent("mousedown", this);
13618         this.fireEvent("click", this);
13619         
13620         this.timer = this.click.defer(this.delay || this.interval, this);
13621     },
13622
13623     // private
13624     click : function(){
13625         this.fireEvent("click", this);
13626         this.timer = this.click.defer(this.getInterval(), this);
13627     },
13628
13629     // private
13630     getInterval: function(){
13631         if(!this.accelerate){
13632             return this.interval;
13633         }
13634         var pressTime = this.mousedownTime.getElapsed();
13635         if(pressTime < 500){
13636             return 400;
13637         }else if(pressTime < 1700){
13638             return 320;
13639         }else if(pressTime < 2600){
13640             return 250;
13641         }else if(pressTime < 3500){
13642             return 180;
13643         }else if(pressTime < 4400){
13644             return 140;
13645         }else if(pressTime < 5300){
13646             return 80;
13647         }else if(pressTime < 6200){
13648             return 50;
13649         }else{
13650             return 10;
13651         }
13652     },
13653
13654     // private
13655     handleMouseOut : function(){
13656         clearTimeout(this.timer);
13657         if(this.pressClass){
13658             this.el.removeClass(this.pressClass);
13659         }
13660         this.el.on("mouseover", this.handleMouseReturn, this);
13661     },
13662
13663     // private
13664     handleMouseReturn : function(){
13665         this.el.un("mouseover", this.handleMouseReturn);
13666         if(this.pressClass){
13667             this.el.addClass(this.pressClass);
13668         }
13669         this.click();
13670     },
13671
13672     // private
13673     handleMouseUp : function(){
13674         clearTimeout(this.timer);
13675         this.el.un("mouseover", this.handleMouseReturn);
13676         this.el.un("mouseout", this.handleMouseOut);
13677         Roo.get(document).un("mouseup", this.handleMouseUp);
13678         this.el.removeClass(this.pressClass);
13679         this.fireEvent("mouseup", this);
13680     }
13681 });/*
13682  * Based on:
13683  * Ext JS Library 1.1.1
13684  * Copyright(c) 2006-2007, Ext JS, LLC.
13685  *
13686  * Originally Released Under LGPL - original licence link has changed is not relivant.
13687  *
13688  * Fork - LGPL
13689  * <script type="text/javascript">
13690  */
13691
13692  
13693 /**
13694  * @class Roo.KeyNav
13695  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13696  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13697  * way to implement custom navigation schemes for any UI component.</p>
13698  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13699  * pageUp, pageDown, del, home, end.  Usage:</p>
13700  <pre><code>
13701 var nav = new Roo.KeyNav("my-element", {
13702     "left" : function(e){
13703         this.moveLeft(e.ctrlKey);
13704     },
13705     "right" : function(e){
13706         this.moveRight(e.ctrlKey);
13707     },
13708     "enter" : function(e){
13709         this.save();
13710     },
13711     scope : this
13712 });
13713 </code></pre>
13714  * @constructor
13715  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13716  * @param {Object} config The config
13717  */
13718 Roo.KeyNav = function(el, config){
13719     this.el = Roo.get(el);
13720     Roo.apply(this, config);
13721     if(!this.disabled){
13722         this.disabled = true;
13723         this.enable();
13724     }
13725 };
13726
13727 Roo.KeyNav.prototype = {
13728     /**
13729      * @cfg {Boolean} disabled
13730      * True to disable this KeyNav instance (defaults to false)
13731      */
13732     disabled : false,
13733     /**
13734      * @cfg {String} defaultEventAction
13735      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13736      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13737      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13738      */
13739     defaultEventAction: "stopEvent",
13740     /**
13741      * @cfg {Boolean} forceKeyDown
13742      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13743      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13744      * handle keydown instead of keypress.
13745      */
13746     forceKeyDown : false,
13747
13748     // private
13749     prepareEvent : function(e){
13750         var k = e.getKey();
13751         var h = this.keyToHandler[k];
13752         //if(h && this[h]){
13753         //    e.stopPropagation();
13754         //}
13755         if(Roo.isSafari && h && k >= 37 && k <= 40){
13756             e.stopEvent();
13757         }
13758     },
13759
13760     // private
13761     relay : function(e){
13762         var k = e.getKey();
13763         var h = this.keyToHandler[k];
13764         if(h && this[h]){
13765             if(this.doRelay(e, this[h], h) !== true){
13766                 e[this.defaultEventAction]();
13767             }
13768         }
13769     },
13770
13771     // private
13772     doRelay : function(e, h, hname){
13773         return h.call(this.scope || this, e);
13774     },
13775
13776     // possible handlers
13777     enter : false,
13778     left : false,
13779     right : false,
13780     up : false,
13781     down : false,
13782     tab : false,
13783     esc : false,
13784     pageUp : false,
13785     pageDown : false,
13786     del : false,
13787     home : false,
13788     end : false,
13789
13790     // quick lookup hash
13791     keyToHandler : {
13792         37 : "left",
13793         39 : "right",
13794         38 : "up",
13795         40 : "down",
13796         33 : "pageUp",
13797         34 : "pageDown",
13798         46 : "del",
13799         36 : "home",
13800         35 : "end",
13801         13 : "enter",
13802         27 : "esc",
13803         9  : "tab"
13804     },
13805
13806         /**
13807          * Enable this KeyNav
13808          */
13809         enable: function(){
13810                 if(this.disabled){
13811             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13812             // the EventObject will normalize Safari automatically
13813             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13814                 this.el.on("keydown", this.relay,  this);
13815             }else{
13816                 this.el.on("keydown", this.prepareEvent,  this);
13817                 this.el.on("keypress", this.relay,  this);
13818             }
13819                     this.disabled = false;
13820                 }
13821         },
13822
13823         /**
13824          * Disable this KeyNav
13825          */
13826         disable: function(){
13827                 if(!this.disabled){
13828                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13829                 this.el.un("keydown", this.relay);
13830             }else{
13831                 this.el.un("keydown", this.prepareEvent);
13832                 this.el.un("keypress", this.relay);
13833             }
13834                     this.disabled = true;
13835                 }
13836         }
13837 };/*
13838  * Based on:
13839  * Ext JS Library 1.1.1
13840  * Copyright(c) 2006-2007, Ext JS, LLC.
13841  *
13842  * Originally Released Under LGPL - original licence link has changed is not relivant.
13843  *
13844  * Fork - LGPL
13845  * <script type="text/javascript">
13846  */
13847
13848  
13849 /**
13850  * @class Roo.KeyMap
13851  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13852  * The constructor accepts the same config object as defined by {@link #addBinding}.
13853  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13854  * combination it will call the function with this signature (if the match is a multi-key
13855  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13856  * A KeyMap can also handle a string representation of keys.<br />
13857  * Usage:
13858  <pre><code>
13859 // map one key by key code
13860 var map = new Roo.KeyMap("my-element", {
13861     key: 13, // or Roo.EventObject.ENTER
13862     fn: myHandler,
13863     scope: myObject
13864 });
13865
13866 // map multiple keys to one action by string
13867 var map = new Roo.KeyMap("my-element", {
13868     key: "a\r\n\t",
13869     fn: myHandler,
13870     scope: myObject
13871 });
13872
13873 // map multiple keys to multiple actions by strings and array of codes
13874 var map = new Roo.KeyMap("my-element", [
13875     {
13876         key: [10,13],
13877         fn: function(){ alert("Return was pressed"); }
13878     }, {
13879         key: "abc",
13880         fn: function(){ alert('a, b or c was pressed'); }
13881     }, {
13882         key: "\t",
13883         ctrl:true,
13884         shift:true,
13885         fn: function(){ alert('Control + shift + tab was pressed.'); }
13886     }
13887 ]);
13888 </code></pre>
13889  * <b>Note: A KeyMap starts enabled</b>
13890  * @constructor
13891  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13892  * @param {Object} config The config (see {@link #addBinding})
13893  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13894  */
13895 Roo.KeyMap = function(el, config, eventName){
13896     this.el  = Roo.get(el);
13897     this.eventName = eventName || "keydown";
13898     this.bindings = [];
13899     if(config){
13900         this.addBinding(config);
13901     }
13902     this.enable();
13903 };
13904
13905 Roo.KeyMap.prototype = {
13906     /**
13907      * True to stop the event from bubbling and prevent the default browser action if the
13908      * key was handled by the KeyMap (defaults to false)
13909      * @type Boolean
13910      */
13911     stopEvent : false,
13912
13913     /**
13914      * Add a new binding to this KeyMap. The following config object properties are supported:
13915      * <pre>
13916 Property    Type             Description
13917 ----------  ---------------  ----------------------------------------------------------------------
13918 key         String/Array     A single keycode or an array of keycodes to handle
13919 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13920 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13921 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13922 fn          Function         The function to call when KeyMap finds the expected key combination
13923 scope       Object           The scope of the callback function
13924 </pre>
13925      *
13926      * Usage:
13927      * <pre><code>
13928 // Create a KeyMap
13929 var map = new Roo.KeyMap(document, {
13930     key: Roo.EventObject.ENTER,
13931     fn: handleKey,
13932     scope: this
13933 });
13934
13935 //Add a new binding to the existing KeyMap later
13936 map.addBinding({
13937     key: 'abc',
13938     shift: true,
13939     fn: handleKey,
13940     scope: this
13941 });
13942 </code></pre>
13943      * @param {Object/Array} config A single KeyMap config or an array of configs
13944      */
13945         addBinding : function(config){
13946         if(config instanceof Array){
13947             for(var i = 0, len = config.length; i < len; i++){
13948                 this.addBinding(config[i]);
13949             }
13950             return;
13951         }
13952         var keyCode = config.key,
13953             shift = config.shift, 
13954             ctrl = config.ctrl, 
13955             alt = config.alt,
13956             fn = config.fn,
13957             scope = config.scope;
13958         if(typeof keyCode == "string"){
13959             var ks = [];
13960             var keyString = keyCode.toUpperCase();
13961             for(var j = 0, len = keyString.length; j < len; j++){
13962                 ks.push(keyString.charCodeAt(j));
13963             }
13964             keyCode = ks;
13965         }
13966         var keyArray = keyCode instanceof Array;
13967         var handler = function(e){
13968             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13969                 var k = e.getKey();
13970                 if(keyArray){
13971                     for(var i = 0, len = keyCode.length; i < len; i++){
13972                         if(keyCode[i] == k){
13973                           if(this.stopEvent){
13974                               e.stopEvent();
13975                           }
13976                           fn.call(scope || window, k, e);
13977                           return;
13978                         }
13979                     }
13980                 }else{
13981                     if(k == keyCode){
13982                         if(this.stopEvent){
13983                            e.stopEvent();
13984                         }
13985                         fn.call(scope || window, k, e);
13986                     }
13987                 }
13988             }
13989         };
13990         this.bindings.push(handler);  
13991         },
13992
13993     /**
13994      * Shorthand for adding a single key listener
13995      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13996      * following options:
13997      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13998      * @param {Function} fn The function to call
13999      * @param {Object} scope (optional) The scope of the function
14000      */
14001     on : function(key, fn, scope){
14002         var keyCode, shift, ctrl, alt;
14003         if(typeof key == "object" && !(key instanceof Array)){
14004             keyCode = key.key;
14005             shift = key.shift;
14006             ctrl = key.ctrl;
14007             alt = key.alt;
14008         }else{
14009             keyCode = key;
14010         }
14011         this.addBinding({
14012             key: keyCode,
14013             shift: shift,
14014             ctrl: ctrl,
14015             alt: alt,
14016             fn: fn,
14017             scope: scope
14018         })
14019     },
14020
14021     // private
14022     handleKeyDown : function(e){
14023             if(this.enabled){ //just in case
14024             var b = this.bindings;
14025             for(var i = 0, len = b.length; i < len; i++){
14026                 b[i].call(this, e);
14027             }
14028             }
14029         },
14030         
14031         /**
14032          * Returns true if this KeyMap is enabled
14033          * @return {Boolean} 
14034          */
14035         isEnabled : function(){
14036             return this.enabled;  
14037         },
14038         
14039         /**
14040          * Enables this KeyMap
14041          */
14042         enable: function(){
14043                 if(!this.enabled){
14044                     this.el.on(this.eventName, this.handleKeyDown, this);
14045                     this.enabled = true;
14046                 }
14047         },
14048
14049         /**
14050          * Disable this KeyMap
14051          */
14052         disable: function(){
14053                 if(this.enabled){
14054                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14055                     this.enabled = false;
14056                 }
14057         }
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.TextMetrics
14072  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14073  * wide, in pixels, a given block of text will be.
14074  * @singleton
14075  */
14076 Roo.util.TextMetrics = function(){
14077     var shared;
14078     return {
14079         /**
14080          * Measures the size of the specified text
14081          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14082          * that can affect the size of the rendered text
14083          * @param {String} text The text to measure
14084          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14085          * in order to accurately measure the text height
14086          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14087          */
14088         measure : function(el, text, fixedWidth){
14089             if(!shared){
14090                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14091             }
14092             shared.bind(el);
14093             shared.setFixedWidth(fixedWidth || 'auto');
14094             return shared.getSize(text);
14095         },
14096
14097         /**
14098          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14099          * the overhead of multiple calls to initialize the style properties on each measurement.
14100          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14101          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14102          * in order to accurately measure the text height
14103          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14104          */
14105         createInstance : function(el, fixedWidth){
14106             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14107         }
14108     };
14109 }();
14110
14111  
14112
14113 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14114     var ml = new Roo.Element(document.createElement('div'));
14115     document.body.appendChild(ml.dom);
14116     ml.position('absolute');
14117     ml.setLeftTop(-1000, -1000);
14118     ml.hide();
14119
14120     if(fixedWidth){
14121         ml.setWidth(fixedWidth);
14122     }
14123      
14124     var instance = {
14125         /**
14126          * Returns the size of the specified text based on the internal element's style and width properties
14127          * @memberOf Roo.util.TextMetrics.Instance#
14128          * @param {String} text The text to measure
14129          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14130          */
14131         getSize : function(text){
14132             ml.update(text);
14133             var s = ml.getSize();
14134             ml.update('');
14135             return s;
14136         },
14137
14138         /**
14139          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14140          * that can affect the size of the rendered text
14141          * @memberOf Roo.util.TextMetrics.Instance#
14142          * @param {String/HTMLElement} el The element, dom node or id
14143          */
14144         bind : function(el){
14145             ml.setStyle(
14146                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14147             );
14148         },
14149
14150         /**
14151          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14152          * to set a fixed width in order to accurately measure the text height.
14153          * @memberOf Roo.util.TextMetrics.Instance#
14154          * @param {Number} width The width to set on the element
14155          */
14156         setFixedWidth : function(width){
14157             ml.setWidth(width);
14158         },
14159
14160         /**
14161          * Returns the measured width of the specified text
14162          * @memberOf Roo.util.TextMetrics.Instance#
14163          * @param {String} text The text to measure
14164          * @return {Number} width The width in pixels
14165          */
14166         getWidth : function(text){
14167             ml.dom.style.width = 'auto';
14168             return this.getSize(text).width;
14169         },
14170
14171         /**
14172          * Returns the measured height of the specified text.  For multiline text, be sure to call
14173          * {@link #setFixedWidth} if necessary.
14174          * @memberOf Roo.util.TextMetrics.Instance#
14175          * @param {String} text The text to measure
14176          * @return {Number} height The height in pixels
14177          */
14178         getHeight : function(text){
14179             return this.getSize(text).height;
14180         }
14181     };
14182
14183     instance.bind(bindTo);
14184
14185     return instance;
14186 };
14187
14188 // backwards compat
14189 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199
14200 /**
14201  * @class Roo.state.Provider
14202  * Abstract base class for state provider implementations. This class provides methods
14203  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14204  * Provider interface.
14205  */
14206 Roo.state.Provider = function(){
14207     /**
14208      * @event statechange
14209      * Fires when a state change occurs.
14210      * @param {Provider} this This state provider
14211      * @param {String} key The state key which was changed
14212      * @param {String} value The encoded value for the state
14213      */
14214     this.addEvents({
14215         "statechange": true
14216     });
14217     this.state = {};
14218     Roo.state.Provider.superclass.constructor.call(this);
14219 };
14220 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14221     /**
14222      * Returns the current value for a key
14223      * @param {String} name The key name
14224      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14225      * @return {Mixed} The state data
14226      */
14227     get : function(name, defaultValue){
14228         return typeof this.state[name] == "undefined" ?
14229             defaultValue : this.state[name];
14230     },
14231     
14232     /**
14233      * Clears a value from the state
14234      * @param {String} name The key name
14235      */
14236     clear : function(name){
14237         delete this.state[name];
14238         this.fireEvent("statechange", this, name, null);
14239     },
14240     
14241     /**
14242      * Sets the value for a key
14243      * @param {String} name The key name
14244      * @param {Mixed} value The value to set
14245      */
14246     set : function(name, value){
14247         this.state[name] = value;
14248         this.fireEvent("statechange", this, name, value);
14249     },
14250     
14251     /**
14252      * Decodes a string previously encoded with {@link #encodeValue}.
14253      * @param {String} value The value to decode
14254      * @return {Mixed} The decoded value
14255      */
14256     decodeValue : function(cookie){
14257         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14258         var matches = re.exec(unescape(cookie));
14259         if(!matches || !matches[1]) return; // non state cookie
14260         var type = matches[1];
14261         var v = matches[2];
14262         switch(type){
14263             case "n":
14264                 return parseFloat(v);
14265             case "d":
14266                 return new Date(Date.parse(v));
14267             case "b":
14268                 return (v == "1");
14269             case "a":
14270                 var all = [];
14271                 var values = v.split("^");
14272                 for(var i = 0, len = values.length; i < len; i++){
14273                     all.push(this.decodeValue(values[i]));
14274                 }
14275                 return all;
14276            case "o":
14277                 var all = {};
14278                 var values = v.split("^");
14279                 for(var i = 0, len = values.length; i < len; i++){
14280                     var kv = values[i].split("=");
14281                     all[kv[0]] = this.decodeValue(kv[1]);
14282                 }
14283                 return all;
14284            default:
14285                 return v;
14286         }
14287     },
14288     
14289     /**
14290      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14291      * @param {Mixed} value The value to encode
14292      * @return {String} The encoded value
14293      */
14294     encodeValue : function(v){
14295         var enc;
14296         if(typeof v == "number"){
14297             enc = "n:" + v;
14298         }else if(typeof v == "boolean"){
14299             enc = "b:" + (v ? "1" : "0");
14300         }else if(v instanceof Date){
14301             enc = "d:" + v.toGMTString();
14302         }else if(v instanceof Array){
14303             var flat = "";
14304             for(var i = 0, len = v.length; i < len; i++){
14305                 flat += this.encodeValue(v[i]);
14306                 if(i != len-1) flat += "^";
14307             }
14308             enc = "a:" + flat;
14309         }else if(typeof v == "object"){
14310             var flat = "";
14311             for(var key in v){
14312                 if(typeof v[key] != "function"){
14313                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14314                 }
14315             }
14316             enc = "o:" + flat.substring(0, flat.length-1);
14317         }else{
14318             enc = "s:" + v;
14319         }
14320         return escape(enc);        
14321     }
14322 });
14323
14324 /*
14325  * Based on:
14326  * Ext JS Library 1.1.1
14327  * Copyright(c) 2006-2007, Ext JS, LLC.
14328  *
14329  * Originally Released Under LGPL - original licence link has changed is not relivant.
14330  *
14331  * Fork - LGPL
14332  * <script type="text/javascript">
14333  */
14334 /**
14335  * @class Roo.state.Manager
14336  * This is the global state manager. By default all components that are "state aware" check this class
14337  * for state information if you don't pass them a custom state provider. In order for this class
14338  * to be useful, it must be initialized with a provider when your application initializes.
14339  <pre><code>
14340 // in your initialization function
14341 init : function(){
14342    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14343    ...
14344    // supposed you have a {@link Roo.BorderLayout}
14345    var layout = new Roo.BorderLayout(...);
14346    layout.restoreState();
14347    // or a {Roo.BasicDialog}
14348    var dialog = new Roo.BasicDialog(...);
14349    dialog.restoreState();
14350  </code></pre>
14351  * @singleton
14352  */
14353 Roo.state.Manager = function(){
14354     var provider = new Roo.state.Provider();
14355     
14356     return {
14357         /**
14358          * Configures the default state provider for your application
14359          * @param {Provider} stateProvider The state provider to set
14360          */
14361         setProvider : function(stateProvider){
14362             provider = stateProvider;
14363         },
14364         
14365         /**
14366          * Returns the current value for a key
14367          * @param {String} name The key name
14368          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14369          * @return {Mixed} The state data
14370          */
14371         get : function(key, defaultValue){
14372             return provider.get(key, defaultValue);
14373         },
14374         
14375         /**
14376          * Sets the value for a key
14377          * @param {String} name The key name
14378          * @param {Mixed} value The state data
14379          */
14380          set : function(key, value){
14381             provider.set(key, value);
14382         },
14383         
14384         /**
14385          * Clears a value from the state
14386          * @param {String} name The key name
14387          */
14388         clear : function(key){
14389             provider.clear(key);
14390         },
14391         
14392         /**
14393          * Gets the currently configured state provider
14394          * @return {Provider} The state provider
14395          */
14396         getProvider : function(){
14397             return provider;
14398         }
14399     };
14400 }();
14401 /*
14402  * Based on:
14403  * Ext JS Library 1.1.1
14404  * Copyright(c) 2006-2007, Ext JS, LLC.
14405  *
14406  * Originally Released Under LGPL - original licence link has changed is not relivant.
14407  *
14408  * Fork - LGPL
14409  * <script type="text/javascript">
14410  */
14411 /**
14412  * @class Roo.state.CookieProvider
14413  * @extends Roo.state.Provider
14414  * The default Provider implementation which saves state via cookies.
14415  * <br />Usage:
14416  <pre><code>
14417    var cp = new Roo.state.CookieProvider({
14418        path: "/cgi-bin/",
14419        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14420        domain: "roojs.com"
14421    })
14422    Roo.state.Manager.setProvider(cp);
14423  </code></pre>
14424  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14425  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14426  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14427  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14428  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14429  * domain the page is running on including the 'www' like 'www.roojs.com')
14430  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14431  * @constructor
14432  * Create a new CookieProvider
14433  * @param {Object} config The configuration object
14434  */
14435 Roo.state.CookieProvider = function(config){
14436     Roo.state.CookieProvider.superclass.constructor.call(this);
14437     this.path = "/";
14438     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14439     this.domain = null;
14440     this.secure = false;
14441     Roo.apply(this, config);
14442     this.state = this.readCookies();
14443 };
14444
14445 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14446     // private
14447     set : function(name, value){
14448         if(typeof value == "undefined" || value === null){
14449             this.clear(name);
14450             return;
14451         }
14452         this.setCookie(name, value);
14453         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14454     },
14455
14456     // private
14457     clear : function(name){
14458         this.clearCookie(name);
14459         Roo.state.CookieProvider.superclass.clear.call(this, name);
14460     },
14461
14462     // private
14463     readCookies : function(){
14464         var cookies = {};
14465         var c = document.cookie + ";";
14466         var re = /\s?(.*?)=(.*?);/g;
14467         var matches;
14468         while((matches = re.exec(c)) != null){
14469             var name = matches[1];
14470             var value = matches[2];
14471             if(name && name.substring(0,3) == "ys-"){
14472                 cookies[name.substr(3)] = this.decodeValue(value);
14473             }
14474         }
14475         return cookies;
14476     },
14477
14478     // private
14479     setCookie : function(name, value){
14480         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14481            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14482            ((this.path == null) ? "" : ("; path=" + this.path)) +
14483            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14484            ((this.secure == true) ? "; secure" : "");
14485     },
14486
14487     // private
14488     clearCookie : function(name){
14489         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14490            ((this.path == null) ? "" : ("; path=" + this.path)) +
14491            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14492            ((this.secure == true) ? "; secure" : "");
14493     }
14494 });/*
14495  * Based on:
14496  * Ext JS Library 1.1.1
14497  * Copyright(c) 2006-2007, Ext JS, LLC.
14498  *
14499  * Originally Released Under LGPL - original licence link has changed is not relivant.
14500  *
14501  * Fork - LGPL
14502  * <script type="text/javascript">
14503  */
14504
14505
14506
14507 /*
14508  * These classes are derivatives of the similarly named classes in the YUI Library.
14509  * The original license:
14510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14511  * Code licensed under the BSD License:
14512  * http://developer.yahoo.net/yui/license.txt
14513  */
14514
14515 (function() {
14516
14517 var Event=Roo.EventManager;
14518 var Dom=Roo.lib.Dom;
14519
14520 /**
14521  * @class Roo.dd.DragDrop
14522  * @extends Roo.util.Observable
14523  * Defines the interface and base operation of items that that can be
14524  * dragged or can be drop targets.  It was designed to be extended, overriding
14525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14526  * Up to three html elements can be associated with a DragDrop instance:
14527  * <ul>
14528  * <li>linked element: the element that is passed into the constructor.
14529  * This is the element which defines the boundaries for interaction with
14530  * other DragDrop objects.</li>
14531  * <li>handle element(s): The drag operation only occurs if the element that
14532  * was clicked matches a handle element.  By default this is the linked
14533  * element, but there are times that you will want only a portion of the
14534  * linked element to initiate the drag operation, and the setHandleElId()
14535  * method provides a way to define this.</li>
14536  * <li>drag element: this represents the element that would be moved along
14537  * with the cursor during a drag operation.  By default, this is the linked
14538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14540  * </li>
14541  * </ul>
14542  * This class should not be instantiated until the onload event to ensure that
14543  * the associated elements are available.
14544  * The following would define a DragDrop obj that would interact with any
14545  * other DragDrop obj in the "group1" group:
14546  * <pre>
14547  *  dd = new Roo.dd.DragDrop("div1", "group1");
14548  * </pre>
14549  * Since none of the event handlers have been implemented, nothing would
14550  * actually happen if you were to run the code above.  Normally you would
14551  * override this class or one of the default implementations, but you can
14552  * also override the methods you want on an instance of the class...
14553  * <pre>
14554  *  dd.onDragDrop = function(e, id) {
14555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14556  *  }
14557  * </pre>
14558  * @constructor
14559  * @param {String} id of the element that is linked to this instance
14560  * @param {String} sGroup the group of related DragDrop objects
14561  * @param {object} config an object containing configurable attributes
14562  *                Valid properties for DragDrop:
14563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14564  */
14565 Roo.dd.DragDrop = function(id, sGroup, config) {
14566     if (id) {
14567         this.init(id, sGroup, config);
14568     }
14569     
14570 };
14571
14572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14573
14574     /**
14575      * The id of the element associated with this object.  This is what we
14576      * refer to as the "linked element" because the size and position of
14577      * this element is used to determine when the drag and drop objects have
14578      * interacted.
14579      * @property id
14580      * @type String
14581      */
14582     id: null,
14583
14584     /**
14585      * Configuration attributes passed into the constructor
14586      * @property config
14587      * @type object
14588      */
14589     config: null,
14590
14591     /**
14592      * The id of the element that will be dragged.  By default this is same
14593      * as the linked element , but could be changed to another element. Ex:
14594      * Roo.dd.DDProxy
14595      * @property dragElId
14596      * @type String
14597      * @private
14598      */
14599     dragElId: null,
14600
14601     /**
14602      * the id of the element that initiates the drag operation.  By default
14603      * this is the linked element, but could be changed to be a child of this
14604      * element.  This lets us do things like only starting the drag when the
14605      * header element within the linked html element is clicked.
14606      * @property handleElId
14607      * @type String
14608      * @private
14609      */
14610     handleElId: null,
14611
14612     /**
14613      * An associative array of HTML tags that will be ignored if clicked.
14614      * @property invalidHandleTypes
14615      * @type {string: string}
14616      */
14617     invalidHandleTypes: null,
14618
14619     /**
14620      * An associative array of ids for elements that will be ignored if clicked
14621      * @property invalidHandleIds
14622      * @type {string: string}
14623      */
14624     invalidHandleIds: null,
14625
14626     /**
14627      * An indexted array of css class names for elements that will be ignored
14628      * if clicked.
14629      * @property invalidHandleClasses
14630      * @type string[]
14631      */
14632     invalidHandleClasses: null,
14633
14634     /**
14635      * The linked element's absolute X position at the time the drag was
14636      * started
14637      * @property startPageX
14638      * @type int
14639      * @private
14640      */
14641     startPageX: 0,
14642
14643     /**
14644      * The linked element's absolute X position at the time the drag was
14645      * started
14646      * @property startPageY
14647      * @type int
14648      * @private
14649      */
14650     startPageY: 0,
14651
14652     /**
14653      * The group defines a logical collection of DragDrop objects that are
14654      * related.  Instances only get events when interacting with other
14655      * DragDrop object in the same group.  This lets us define multiple
14656      * groups using a single DragDrop subclass if we want.
14657      * @property groups
14658      * @type {string: string}
14659      */
14660     groups: null,
14661
14662     /**
14663      * Individual drag/drop instances can be locked.  This will prevent
14664      * onmousedown start drag.
14665      * @property locked
14666      * @type boolean
14667      * @private
14668      */
14669     locked: false,
14670
14671     /**
14672      * Lock this instance
14673      * @method lock
14674      */
14675     lock: function() { this.locked = true; },
14676
14677     /**
14678      * Unlock this instace
14679      * @method unlock
14680      */
14681     unlock: function() { this.locked = false; },
14682
14683     /**
14684      * By default, all insances can be a drop target.  This can be disabled by
14685      * setting isTarget to false.
14686      * @method isTarget
14687      * @type boolean
14688      */
14689     isTarget: true,
14690
14691     /**
14692      * The padding configured for this drag and drop object for calculating
14693      * the drop zone intersection with this object.
14694      * @method padding
14695      * @type int[]
14696      */
14697     padding: null,
14698
14699     /**
14700      * Cached reference to the linked element
14701      * @property _domRef
14702      * @private
14703      */
14704     _domRef: null,
14705
14706     /**
14707      * Internal typeof flag
14708      * @property __ygDragDrop
14709      * @private
14710      */
14711     __ygDragDrop: true,
14712
14713     /**
14714      * Set to true when horizontal contraints are applied
14715      * @property constrainX
14716      * @type boolean
14717      * @private
14718      */
14719     constrainX: false,
14720
14721     /**
14722      * Set to true when vertical contraints are applied
14723      * @property constrainY
14724      * @type boolean
14725      * @private
14726      */
14727     constrainY: false,
14728
14729     /**
14730      * The left constraint
14731      * @property minX
14732      * @type int
14733      * @private
14734      */
14735     minX: 0,
14736
14737     /**
14738      * The right constraint
14739      * @property maxX
14740      * @type int
14741      * @private
14742      */
14743     maxX: 0,
14744
14745     /**
14746      * The up constraint
14747      * @property minY
14748      * @type int
14749      * @type int
14750      * @private
14751      */
14752     minY: 0,
14753
14754     /**
14755      * The down constraint
14756      * @property maxY
14757      * @type int
14758      * @private
14759      */
14760     maxY: 0,
14761
14762     /**
14763      * Maintain offsets when we resetconstraints.  Set to true when you want
14764      * the position of the element relative to its parent to stay the same
14765      * when the page changes
14766      *
14767      * @property maintainOffset
14768      * @type boolean
14769      */
14770     maintainOffset: false,
14771
14772     /**
14773      * Array of pixel locations the element will snap to if we specified a
14774      * horizontal graduation/interval.  This array is generated automatically
14775      * when you define a tick interval.
14776      * @property xTicks
14777      * @type int[]
14778      */
14779     xTicks: null,
14780
14781     /**
14782      * Array of pixel locations the element will snap to if we specified a
14783      * vertical graduation/interval.  This array is generated automatically
14784      * when you define a tick interval.
14785      * @property yTicks
14786      * @type int[]
14787      */
14788     yTicks: null,
14789
14790     /**
14791      * By default the drag and drop instance will only respond to the primary
14792      * button click (left button for a right-handed mouse).  Set to true to
14793      * allow drag and drop to start with any mouse click that is propogated
14794      * by the browser
14795      * @property primaryButtonOnly
14796      * @type boolean
14797      */
14798     primaryButtonOnly: true,
14799
14800     /**
14801      * The availabe property is false until the linked dom element is accessible.
14802      * @property available
14803      * @type boolean
14804      */
14805     available: false,
14806
14807     /**
14808      * By default, drags can only be initiated if the mousedown occurs in the
14809      * region the linked element is.  This is done in part to work around a
14810      * bug in some browsers that mis-report the mousedown if the previous
14811      * mouseup happened outside of the window.  This property is set to true
14812      * if outer handles are defined.
14813      *
14814      * @property hasOuterHandles
14815      * @type boolean
14816      * @default false
14817      */
14818     hasOuterHandles: false,
14819
14820     /**
14821      * Code that executes immediately before the startDrag event
14822      * @method b4StartDrag
14823      * @private
14824      */
14825     b4StartDrag: function(x, y) { },
14826
14827     /**
14828      * Abstract method called after a drag/drop object is clicked
14829      * and the drag or mousedown time thresholds have beeen met.
14830      * @method startDrag
14831      * @param {int} X click location
14832      * @param {int} Y click location
14833      */
14834     startDrag: function(x, y) { /* override this */ },
14835
14836     /**
14837      * Code that executes immediately before the onDrag event
14838      * @method b4Drag
14839      * @private
14840      */
14841     b4Drag: function(e) { },
14842
14843     /**
14844      * Abstract method called during the onMouseMove event while dragging an
14845      * object.
14846      * @method onDrag
14847      * @param {Event} e the mousemove event
14848      */
14849     onDrag: function(e) { /* override this */ },
14850
14851     /**
14852      * Abstract method called when this element fist begins hovering over
14853      * another DragDrop obj
14854      * @method onDragEnter
14855      * @param {Event} e the mousemove event
14856      * @param {String|DragDrop[]} id In POINT mode, the element
14857      * id this is hovering over.  In INTERSECT mode, an array of one or more
14858      * dragdrop items being hovered over.
14859      */
14860     onDragEnter: function(e, id) { /* override this */ },
14861
14862     /**
14863      * Code that executes immediately before the onDragOver event
14864      * @method b4DragOver
14865      * @private
14866      */
14867     b4DragOver: function(e) { },
14868
14869     /**
14870      * Abstract method called when this element is hovering over another
14871      * DragDrop obj
14872      * @method onDragOver
14873      * @param {Event} e the mousemove event
14874      * @param {String|DragDrop[]} id In POINT mode, the element
14875      * id this is hovering over.  In INTERSECT mode, an array of dd items
14876      * being hovered over.
14877      */
14878     onDragOver: function(e, id) { /* override this */ },
14879
14880     /**
14881      * Code that executes immediately before the onDragOut event
14882      * @method b4DragOut
14883      * @private
14884      */
14885     b4DragOut: function(e) { },
14886
14887     /**
14888      * Abstract method called when we are no longer hovering over an element
14889      * @method onDragOut
14890      * @param {Event} e the mousemove event
14891      * @param {String|DragDrop[]} id In POINT mode, the element
14892      * id this was hovering over.  In INTERSECT mode, an array of dd items
14893      * that the mouse is no longer over.
14894      */
14895     onDragOut: function(e, id) { /* override this */ },
14896
14897     /**
14898      * Code that executes immediately before the onDragDrop event
14899      * @method b4DragDrop
14900      * @private
14901      */
14902     b4DragDrop: function(e) { },
14903
14904     /**
14905      * Abstract method called when this item is dropped on another DragDrop
14906      * obj
14907      * @method onDragDrop
14908      * @param {Event} e the mouseup event
14909      * @param {String|DragDrop[]} id In POINT mode, the element
14910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14911      * was dropped on.
14912      */
14913     onDragDrop: function(e, id) { /* override this */ },
14914
14915     /**
14916      * Abstract method called when this item is dropped on an area with no
14917      * drop target
14918      * @method onInvalidDrop
14919      * @param {Event} e the mouseup event
14920      */
14921     onInvalidDrop: function(e) { /* override this */ },
14922
14923     /**
14924      * Code that executes immediately before the endDrag event
14925      * @method b4EndDrag
14926      * @private
14927      */
14928     b4EndDrag: function(e) { },
14929
14930     /**
14931      * Fired when we are done dragging the object
14932      * @method endDrag
14933      * @param {Event} e the mouseup event
14934      */
14935     endDrag: function(e) { /* override this */ },
14936
14937     /**
14938      * Code executed immediately before the onMouseDown event
14939      * @method b4MouseDown
14940      * @param {Event} e the mousedown event
14941      * @private
14942      */
14943     b4MouseDown: function(e) {  },
14944
14945     /**
14946      * Event handler that fires when a drag/drop obj gets a mousedown
14947      * @method onMouseDown
14948      * @param {Event} e the mousedown event
14949      */
14950     onMouseDown: function(e) { /* override this */ },
14951
14952     /**
14953      * Event handler that fires when a drag/drop obj gets a mouseup
14954      * @method onMouseUp
14955      * @param {Event} e the mouseup event
14956      */
14957     onMouseUp: function(e) { /* override this */ },
14958
14959     /**
14960      * Override the onAvailable method to do what is needed after the initial
14961      * position was determined.
14962      * @method onAvailable
14963      */
14964     onAvailable: function () {
14965     },
14966
14967     /*
14968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14969      * @type Object
14970      */
14971     defaultPadding : {left:0, right:0, top:0, bottom:0},
14972
14973     /*
14974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14975  *
14976  * Usage:
14977  <pre><code>
14978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14979                 { dragElId: "existingProxyDiv" });
14980  dd.startDrag = function(){
14981      this.constrainTo("parent-id");
14982  };
14983  </code></pre>
14984  * Or you can initalize it using the {@link Roo.Element} object:
14985  <pre><code>
14986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14987      startDrag : function(){
14988          this.constrainTo("parent-id");
14989      }
14990  });
14991  </code></pre>
14992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14995      * an object containing the sides to pad. For example: {right:10, bottom:10}
14996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14997      */
14998     constrainTo : function(constrainTo, pad, inContent){
14999         if(typeof pad == "number"){
15000             pad = {left: pad, right:pad, top:pad, bottom:pad};
15001         }
15002         pad = pad || this.defaultPadding;
15003         var b = Roo.get(this.getEl()).getBox();
15004         var ce = Roo.get(constrainTo);
15005         var s = ce.getScroll();
15006         var c, cd = ce.dom;
15007         if(cd == document.body){
15008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
15009         }else{
15010             xy = ce.getXY();
15011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
15012         }
15013
15014
15015         var topSpace = b.y - c.y;
15016         var leftSpace = b.x - c.x;
15017
15018         this.resetConstraints();
15019         this.setXConstraint(leftSpace - (pad.left||0), // left
15020                 c.width - leftSpace - b.width - (pad.right||0) //right
15021         );
15022         this.setYConstraint(topSpace - (pad.top||0), //top
15023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
15024         );
15025     },
15026
15027     /**
15028      * Returns a reference to the linked element
15029      * @method getEl
15030      * @return {HTMLElement} the html element
15031      */
15032     getEl: function() {
15033         if (!this._domRef) {
15034             this._domRef = Roo.getDom(this.id);
15035         }
15036
15037         return this._domRef;
15038     },
15039
15040     /**
15041      * Returns a reference to the actual element to drag.  By default this is
15042      * the same as the html element, but it can be assigned to another
15043      * element. An example of this can be found in Roo.dd.DDProxy
15044      * @method getDragEl
15045      * @return {HTMLElement} the html element
15046      */
15047     getDragEl: function() {
15048         return Roo.getDom(this.dragElId);
15049     },
15050
15051     /**
15052      * Sets up the DragDrop object.  Must be called in the constructor of any
15053      * Roo.dd.DragDrop subclass
15054      * @method init
15055      * @param id the id of the linked element
15056      * @param {String} sGroup the group of related items
15057      * @param {object} config configuration attributes
15058      */
15059     init: function(id, sGroup, config) {
15060         this.initTarget(id, sGroup, config);
15061         Event.on(this.id, "mousedown", this.handleMouseDown, this);
15062         // Event.on(this.id, "selectstart", Event.preventDefault);
15063     },
15064
15065     /**
15066      * Initializes Targeting functionality only... the object does not
15067      * get a mousedown handler.
15068      * @method initTarget
15069      * @param id the id of the linked element
15070      * @param {String} sGroup the group of related items
15071      * @param {object} config configuration attributes
15072      */
15073     initTarget: function(id, sGroup, config) {
15074
15075         // configuration attributes
15076         this.config = config || {};
15077
15078         // create a local reference to the drag and drop manager
15079         this.DDM = Roo.dd.DDM;
15080         // initialize the groups array
15081         this.groups = {};
15082
15083         // assume that we have an element reference instead of an id if the
15084         // parameter is not a string
15085         if (typeof id !== "string") {
15086             id = Roo.id(id);
15087         }
15088
15089         // set the id
15090         this.id = id;
15091
15092         // add to an interaction group
15093         this.addToGroup((sGroup) ? sGroup : "default");
15094
15095         // We don't want to register this as the handle with the manager
15096         // so we just set the id rather than calling the setter.
15097         this.handleElId = id;
15098
15099         // the linked element is the element that gets dragged by default
15100         this.setDragElId(id);
15101
15102         // by default, clicked anchors will not start drag operations.
15103         this.invalidHandleTypes = { A: "A" };
15104         this.invalidHandleIds = {};
15105         this.invalidHandleClasses = [];
15106
15107         this.applyConfig();
15108
15109         this.handleOnAvailable();
15110     },
15111
15112     /**
15113      * Applies the configuration parameters that were passed into the constructor.
15114      * This is supposed to happen at each level through the inheritance chain.  So
15115      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15116      * DragDrop in order to get all of the parameters that are available in
15117      * each object.
15118      * @method applyConfig
15119      */
15120     applyConfig: function() {
15121
15122         // configurable properties:
15123         //    padding, isTarget, maintainOffset, primaryButtonOnly
15124         this.padding           = this.config.padding || [0, 0, 0, 0];
15125         this.isTarget          = (this.config.isTarget !== false);
15126         this.maintainOffset    = (this.config.maintainOffset);
15127         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15128
15129     },
15130
15131     /**
15132      * Executed when the linked element is available
15133      * @method handleOnAvailable
15134      * @private
15135      */
15136     handleOnAvailable: function() {
15137         this.available = true;
15138         this.resetConstraints();
15139         this.onAvailable();
15140     },
15141
15142      /**
15143      * Configures the padding for the target zone in px.  Effectively expands
15144      * (or reduces) the virtual object size for targeting calculations.
15145      * Supports css-style shorthand; if only one parameter is passed, all sides
15146      * will have that padding, and if only two are passed, the top and bottom
15147      * will have the first param, the left and right the second.
15148      * @method setPadding
15149      * @param {int} iTop    Top pad
15150      * @param {int} iRight  Right pad
15151      * @param {int} iBot    Bot pad
15152      * @param {int} iLeft   Left pad
15153      */
15154     setPadding: function(iTop, iRight, iBot, iLeft) {
15155         // this.padding = [iLeft, iRight, iTop, iBot];
15156         if (!iRight && 0 !== iRight) {
15157             this.padding = [iTop, iTop, iTop, iTop];
15158         } else if (!iBot && 0 !== iBot) {
15159             this.padding = [iTop, iRight, iTop, iRight];
15160         } else {
15161             this.padding = [iTop, iRight, iBot, iLeft];
15162         }
15163     },
15164
15165     /**
15166      * Stores the initial placement of the linked element.
15167      * @method setInitialPosition
15168      * @param {int} diffX   the X offset, default 0
15169      * @param {int} diffY   the Y offset, default 0
15170      */
15171     setInitPosition: function(diffX, diffY) {
15172         var el = this.getEl();
15173
15174         if (!this.DDM.verifyEl(el)) {
15175             return;
15176         }
15177
15178         var dx = diffX || 0;
15179         var dy = diffY || 0;
15180
15181         var p = Dom.getXY( el );
15182
15183         this.initPageX = p[0] - dx;
15184         this.initPageY = p[1] - dy;
15185
15186         this.lastPageX = p[0];
15187         this.lastPageY = p[1];
15188
15189
15190         this.setStartPosition(p);
15191     },
15192
15193     /**
15194      * Sets the start position of the element.  This is set when the obj
15195      * is initialized, the reset when a drag is started.
15196      * @method setStartPosition
15197      * @param pos current position (from previous lookup)
15198      * @private
15199      */
15200     setStartPosition: function(pos) {
15201         var p = pos || Dom.getXY( this.getEl() );
15202         this.deltaSetXY = null;
15203
15204         this.startPageX = p[0];
15205         this.startPageY = p[1];
15206     },
15207
15208     /**
15209      * Add this instance to a group of related drag/drop objects.  All
15210      * instances belong to at least one group, and can belong to as many
15211      * groups as needed.
15212      * @method addToGroup
15213      * @param sGroup {string} the name of the group
15214      */
15215     addToGroup: function(sGroup) {
15216         this.groups[sGroup] = true;
15217         this.DDM.regDragDrop(this, sGroup);
15218     },
15219
15220     /**
15221      * Remove's this instance from the supplied interaction group
15222      * @method removeFromGroup
15223      * @param {string}  sGroup  The group to drop
15224      */
15225     removeFromGroup: function(sGroup) {
15226         if (this.groups[sGroup]) {
15227             delete this.groups[sGroup];
15228         }
15229
15230         this.DDM.removeDDFromGroup(this, sGroup);
15231     },
15232
15233     /**
15234      * Allows you to specify that an element other than the linked element
15235      * will be moved with the cursor during a drag
15236      * @method setDragElId
15237      * @param id {string} the id of the element that will be used to initiate the drag
15238      */
15239     setDragElId: function(id) {
15240         this.dragElId = id;
15241     },
15242
15243     /**
15244      * Allows you to specify a child of the linked element that should be
15245      * used to initiate the drag operation.  An example of this would be if
15246      * you have a content div with text and links.  Clicking anywhere in the
15247      * content area would normally start the drag operation.  Use this method
15248      * to specify that an element inside of the content div is the element
15249      * that starts the drag operation.
15250      * @method setHandleElId
15251      * @param id {string} the id of the element that will be used to
15252      * initiate the drag.
15253      */
15254     setHandleElId: function(id) {
15255         if (typeof id !== "string") {
15256             id = Roo.id(id);
15257         }
15258         this.handleElId = id;
15259         this.DDM.regHandle(this.id, id);
15260     },
15261
15262     /**
15263      * Allows you to set an element outside of the linked element as a drag
15264      * handle
15265      * @method setOuterHandleElId
15266      * @param id the id of the element that will be used to initiate the drag
15267      */
15268     setOuterHandleElId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         Event.on(id, "mousedown",
15273                 this.handleMouseDown, this);
15274         this.setHandleElId(id);
15275
15276         this.hasOuterHandles = true;
15277     },
15278
15279     /**
15280      * Remove all drag and drop hooks for this element
15281      * @method unreg
15282      */
15283     unreg: function() {
15284         Event.un(this.id, "mousedown",
15285                 this.handleMouseDown);
15286         this._domRef = null;
15287         this.DDM._remove(this);
15288     },
15289
15290     destroy : function(){
15291         this.unreg();
15292     },
15293
15294     /**
15295      * Returns true if this instance is locked, or the drag drop mgr is locked
15296      * (meaning that all drag/drop is disabled on the page.)
15297      * @method isLocked
15298      * @return {boolean} true if this obj or all drag/drop is locked, else
15299      * false
15300      */
15301     isLocked: function() {
15302         return (this.DDM.isLocked() || this.locked);
15303     },
15304
15305     /**
15306      * Fired when this object is clicked
15307      * @method handleMouseDown
15308      * @param {Event} e
15309      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15310      * @private
15311      */
15312     handleMouseDown: function(e, oDD){
15313         if (this.primaryButtonOnly && e.button != 0) {
15314             return;
15315         }
15316
15317         if (this.isLocked()) {
15318             return;
15319         }
15320
15321         this.DDM.refreshCache(this.groups);
15322
15323         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15324         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15325         } else {
15326             if (this.clickValidator(e)) {
15327
15328                 // set the initial element position
15329                 this.setStartPosition();
15330
15331
15332                 this.b4MouseDown(e);
15333                 this.onMouseDown(e);
15334
15335                 this.DDM.handleMouseDown(e, this);
15336
15337                 this.DDM.stopEvent(e);
15338             } else {
15339
15340
15341             }
15342         }
15343     },
15344
15345     clickValidator: function(e) {
15346         var target = e.getTarget();
15347         return ( this.isValidHandleChild(target) &&
15348                     (this.id == this.handleElId ||
15349                         this.DDM.handleWasClicked(target, this.id)) );
15350     },
15351
15352     /**
15353      * Allows you to specify a tag name that should not start a drag operation
15354      * when clicked.  This is designed to facilitate embedding links within a
15355      * drag handle that do something other than start the drag.
15356      * @method addInvalidHandleType
15357      * @param {string} tagName the type of element to exclude
15358      */
15359     addInvalidHandleType: function(tagName) {
15360         var type = tagName.toUpperCase();
15361         this.invalidHandleTypes[type] = type;
15362     },
15363
15364     /**
15365      * Lets you to specify an element id for a child of a drag handle
15366      * that should not initiate a drag
15367      * @method addInvalidHandleId
15368      * @param {string} id the element id of the element you wish to ignore
15369      */
15370     addInvalidHandleId: function(id) {
15371         if (typeof id !== "string") {
15372             id = Roo.id(id);
15373         }
15374         this.invalidHandleIds[id] = id;
15375     },
15376
15377     /**
15378      * Lets you specify a css class of elements that will not initiate a drag
15379      * @method addInvalidHandleClass
15380      * @param {string} cssClass the class of the elements you wish to ignore
15381      */
15382     addInvalidHandleClass: function(cssClass) {
15383         this.invalidHandleClasses.push(cssClass);
15384     },
15385
15386     /**
15387      * Unsets an excluded tag name set by addInvalidHandleType
15388      * @method removeInvalidHandleType
15389      * @param {string} tagName the type of element to unexclude
15390      */
15391     removeInvalidHandleType: function(tagName) {
15392         var type = tagName.toUpperCase();
15393         // this.invalidHandleTypes[type] = null;
15394         delete this.invalidHandleTypes[type];
15395     },
15396
15397     /**
15398      * Unsets an invalid handle id
15399      * @method removeInvalidHandleId
15400      * @param {string} id the id of the element to re-enable
15401      */
15402     removeInvalidHandleId: function(id) {
15403         if (typeof id !== "string") {
15404             id = Roo.id(id);
15405         }
15406         delete this.invalidHandleIds[id];
15407     },
15408
15409     /**
15410      * Unsets an invalid css class
15411      * @method removeInvalidHandleClass
15412      * @param {string} cssClass the class of the element(s) you wish to
15413      * re-enable
15414      */
15415     removeInvalidHandleClass: function(cssClass) {
15416         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15417             if (this.invalidHandleClasses[i] == cssClass) {
15418                 delete this.invalidHandleClasses[i];
15419             }
15420         }
15421     },
15422
15423     /**
15424      * Checks the tag exclusion list to see if this click should be ignored
15425      * @method isValidHandleChild
15426      * @param {HTMLElement} node the HTMLElement to evaluate
15427      * @return {boolean} true if this is a valid tag type, false if not
15428      */
15429     isValidHandleChild: function(node) {
15430
15431         var valid = true;
15432         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15433         var nodeName;
15434         try {
15435             nodeName = node.nodeName.toUpperCase();
15436         } catch(e) {
15437             nodeName = node.nodeName;
15438         }
15439         valid = valid && !this.invalidHandleTypes[nodeName];
15440         valid = valid && !this.invalidHandleIds[node.id];
15441
15442         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15443             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15444         }
15445
15446
15447         return valid;
15448
15449     },
15450
15451     /**
15452      * Create the array of horizontal tick marks if an interval was specified
15453      * in setXConstraint().
15454      * @method setXTicks
15455      * @private
15456      */
15457     setXTicks: function(iStartX, iTickSize) {
15458         this.xTicks = [];
15459         this.xTickSize = iTickSize;
15460
15461         var tickMap = {};
15462
15463         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15464             if (!tickMap[i]) {
15465                 this.xTicks[this.xTicks.length] = i;
15466                 tickMap[i] = true;
15467             }
15468         }
15469
15470         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15471             if (!tickMap[i]) {
15472                 this.xTicks[this.xTicks.length] = i;
15473                 tickMap[i] = true;
15474             }
15475         }
15476
15477         this.xTicks.sort(this.DDM.numericSort) ;
15478     },
15479
15480     /**
15481      * Create the array of vertical tick marks if an interval was specified in
15482      * setYConstraint().
15483      * @method setYTicks
15484      * @private
15485      */
15486     setYTicks: function(iStartY, iTickSize) {
15487         this.yTicks = [];
15488         this.yTickSize = iTickSize;
15489
15490         var tickMap = {};
15491
15492         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15493             if (!tickMap[i]) {
15494                 this.yTicks[this.yTicks.length] = i;
15495                 tickMap[i] = true;
15496             }
15497         }
15498
15499         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15500             if (!tickMap[i]) {
15501                 this.yTicks[this.yTicks.length] = i;
15502                 tickMap[i] = true;
15503             }
15504         }
15505
15506         this.yTicks.sort(this.DDM.numericSort) ;
15507     },
15508
15509     /**
15510      * By default, the element can be dragged any place on the screen.  Use
15511      * this method to limit the horizontal travel of the element.  Pass in
15512      * 0,0 for the parameters if you want to lock the drag to the y axis.
15513      * @method setXConstraint
15514      * @param {int} iLeft the number of pixels the element can move to the left
15515      * @param {int} iRight the number of pixels the element can move to the
15516      * right
15517      * @param {int} iTickSize optional parameter for specifying that the
15518      * element
15519      * should move iTickSize pixels at a time.
15520      */
15521     setXConstraint: function(iLeft, iRight, iTickSize) {
15522         this.leftConstraint = iLeft;
15523         this.rightConstraint = iRight;
15524
15525         this.minX = this.initPageX - iLeft;
15526         this.maxX = this.initPageX + iRight;
15527         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15528
15529         this.constrainX = true;
15530     },
15531
15532     /**
15533      * Clears any constraints applied to this instance.  Also clears ticks
15534      * since they can't exist independent of a constraint at this time.
15535      * @method clearConstraints
15536      */
15537     clearConstraints: function() {
15538         this.constrainX = false;
15539         this.constrainY = false;
15540         this.clearTicks();
15541     },
15542
15543     /**
15544      * Clears any tick interval defined for this instance
15545      * @method clearTicks
15546      */
15547     clearTicks: function() {
15548         this.xTicks = null;
15549         this.yTicks = null;
15550         this.xTickSize = 0;
15551         this.yTickSize = 0;
15552     },
15553
15554     /**
15555      * By default, the element can be dragged any place on the screen.  Set
15556      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15557      * parameters if you want to lock the drag to the x axis.
15558      * @method setYConstraint
15559      * @param {int} iUp the number of pixels the element can move up
15560      * @param {int} iDown the number of pixels the element can move down
15561      * @param {int} iTickSize optional parameter for specifying that the
15562      * element should move iTickSize pixels at a time.
15563      */
15564     setYConstraint: function(iUp, iDown, iTickSize) {
15565         this.topConstraint = iUp;
15566         this.bottomConstraint = iDown;
15567
15568         this.minY = this.initPageY - iUp;
15569         this.maxY = this.initPageY + iDown;
15570         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15571
15572         this.constrainY = true;
15573
15574     },
15575
15576     /**
15577      * resetConstraints must be called if you manually reposition a dd element.
15578      * @method resetConstraints
15579      * @param {boolean} maintainOffset
15580      */
15581     resetConstraints: function() {
15582
15583
15584         // Maintain offsets if necessary
15585         if (this.initPageX || this.initPageX === 0) {
15586             // figure out how much this thing has moved
15587             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15588             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15589
15590             this.setInitPosition(dx, dy);
15591
15592         // This is the first time we have detected the element's position
15593         } else {
15594             this.setInitPosition();
15595         }
15596
15597         if (this.constrainX) {
15598             this.setXConstraint( this.leftConstraint,
15599                                  this.rightConstraint,
15600                                  this.xTickSize        );
15601         }
15602
15603         if (this.constrainY) {
15604             this.setYConstraint( this.topConstraint,
15605                                  this.bottomConstraint,
15606                                  this.yTickSize         );
15607         }
15608     },
15609
15610     /**
15611      * Normally the drag element is moved pixel by pixel, but we can specify
15612      * that it move a number of pixels at a time.  This method resolves the
15613      * location when we have it set up like this.
15614      * @method getTick
15615      * @param {int} val where we want to place the object
15616      * @param {int[]} tickArray sorted array of valid points
15617      * @return {int} the closest tick
15618      * @private
15619      */
15620     getTick: function(val, tickArray) {
15621
15622         if (!tickArray) {
15623             // If tick interval is not defined, it is effectively 1 pixel,
15624             // so we return the value passed to us.
15625             return val;
15626         } else if (tickArray[0] >= val) {
15627             // The value is lower than the first tick, so we return the first
15628             // tick.
15629             return tickArray[0];
15630         } else {
15631             for (var i=0, len=tickArray.length; i<len; ++i) {
15632                 var next = i + 1;
15633                 if (tickArray[next] && tickArray[next] >= val) {
15634                     var diff1 = val - tickArray[i];
15635                     var diff2 = tickArray[next] - val;
15636                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15637                 }
15638             }
15639
15640             // The value is larger than the last tick, so we return the last
15641             // tick.
15642             return tickArray[tickArray.length - 1];
15643         }
15644     },
15645
15646     /**
15647      * toString method
15648      * @method toString
15649      * @return {string} string representation of the dd obj
15650      */
15651     toString: function() {
15652         return ("DragDrop " + this.id);
15653     }
15654
15655 });
15656
15657 })();
15658 /*
15659  * Based on:
15660  * Ext JS Library 1.1.1
15661  * Copyright(c) 2006-2007, Ext JS, LLC.
15662  *
15663  * Originally Released Under LGPL - original licence link has changed is not relivant.
15664  *
15665  * Fork - LGPL
15666  * <script type="text/javascript">
15667  */
15668
15669
15670 /**
15671  * The drag and drop utility provides a framework for building drag and drop
15672  * applications.  In addition to enabling drag and drop for specific elements,
15673  * the drag and drop elements are tracked by the manager class, and the
15674  * interactions between the various elements are tracked during the drag and
15675  * the implementing code is notified about these important moments.
15676  */
15677
15678 // Only load the library once.  Rewriting the manager class would orphan
15679 // existing drag and drop instances.
15680 if (!Roo.dd.DragDropMgr) {
15681
15682 /**
15683  * @class Roo.dd.DragDropMgr
15684  * DragDropMgr is a singleton that tracks the element interaction for
15685  * all DragDrop items in the window.  Generally, you will not call
15686  * this class directly, but it does have helper methods that could
15687  * be useful in your DragDrop implementations.
15688  * @singleton
15689  */
15690 Roo.dd.DragDropMgr = function() {
15691
15692     var Event = Roo.EventManager;
15693
15694     return {
15695
15696         /**
15697          * Two dimensional Array of registered DragDrop objects.  The first
15698          * dimension is the DragDrop item group, the second the DragDrop
15699          * object.
15700          * @property ids
15701          * @type {string: string}
15702          * @private
15703          * @static
15704          */
15705         ids: {},
15706
15707         /**
15708          * Array of element ids defined as drag handles.  Used to determine
15709          * if the element that generated the mousedown event is actually the
15710          * handle and not the html element itself.
15711          * @property handleIds
15712          * @type {string: string}
15713          * @private
15714          * @static
15715          */
15716         handleIds: {},
15717
15718         /**
15719          * the DragDrop object that is currently being dragged
15720          * @property dragCurrent
15721          * @type DragDrop
15722          * @private
15723          * @static
15724          **/
15725         dragCurrent: null,
15726
15727         /**
15728          * the DragDrop object(s) that are being hovered over
15729          * @property dragOvers
15730          * @type Array
15731          * @private
15732          * @static
15733          */
15734         dragOvers: {},
15735
15736         /**
15737          * the X distance between the cursor and the object being dragged
15738          * @property deltaX
15739          * @type int
15740          * @private
15741          * @static
15742          */
15743         deltaX: 0,
15744
15745         /**
15746          * the Y distance between the cursor and the object being dragged
15747          * @property deltaY
15748          * @type int
15749          * @private
15750          * @static
15751          */
15752         deltaY: 0,
15753
15754         /**
15755          * Flag to determine if we should prevent the default behavior of the
15756          * events we define. By default this is true, but this can be set to
15757          * false if you need the default behavior (not recommended)
15758          * @property preventDefault
15759          * @type boolean
15760          * @static
15761          */
15762         preventDefault: true,
15763
15764         /**
15765          * Flag to determine if we should stop the propagation of the events
15766          * we generate. This is true by default but you may want to set it to
15767          * false if the html element contains other features that require the
15768          * mouse click.
15769          * @property stopPropagation
15770          * @type boolean
15771          * @static
15772          */
15773         stopPropagation: true,
15774
15775         /**
15776          * Internal flag that is set to true when drag and drop has been
15777          * intialized
15778          * @property initialized
15779          * @private
15780          * @static
15781          */
15782         initalized: false,
15783
15784         /**
15785          * All drag and drop can be disabled.
15786          * @property locked
15787          * @private
15788          * @static
15789          */
15790         locked: false,
15791
15792         /**
15793          * Called the first time an element is registered.
15794          * @method init
15795          * @private
15796          * @static
15797          */
15798         init: function() {
15799             this.initialized = true;
15800         },
15801
15802         /**
15803          * In point mode, drag and drop interaction is defined by the
15804          * location of the cursor during the drag/drop
15805          * @property POINT
15806          * @type int
15807          * @static
15808          */
15809         POINT: 0,
15810
15811         /**
15812          * In intersect mode, drag and drop interactio nis defined by the
15813          * overlap of two or more drag and drop objects.
15814          * @property INTERSECT
15815          * @type int
15816          * @static
15817          */
15818         INTERSECT: 1,
15819
15820         /**
15821          * The current drag and drop mode.  Default: POINT
15822          * @property mode
15823          * @type int
15824          * @static
15825          */
15826         mode: 0,
15827
15828         /**
15829          * Runs method on all drag and drop objects
15830          * @method _execOnAll
15831          * @private
15832          * @static
15833          */
15834         _execOnAll: function(sMethod, args) {
15835             for (var i in this.ids) {
15836                 for (var j in this.ids[i]) {
15837                     var oDD = this.ids[i][j];
15838                     if (! this.isTypeOfDD(oDD)) {
15839                         continue;
15840                     }
15841                     oDD[sMethod].apply(oDD, args);
15842                 }
15843             }
15844         },
15845
15846         /**
15847          * Drag and drop initialization.  Sets up the global event handlers
15848          * @method _onLoad
15849          * @private
15850          * @static
15851          */
15852         _onLoad: function() {
15853
15854             this.init();
15855
15856
15857             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15858             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15859             Event.on(window,   "unload",    this._onUnload, this, true);
15860             Event.on(window,   "resize",    this._onResize, this, true);
15861             // Event.on(window,   "mouseout",    this._test);
15862
15863         },
15864
15865         /**
15866          * Reset constraints on all drag and drop objs
15867          * @method _onResize
15868          * @private
15869          * @static
15870          */
15871         _onResize: function(e) {
15872             this._execOnAll("resetConstraints", []);
15873         },
15874
15875         /**
15876          * Lock all drag and drop functionality
15877          * @method lock
15878          * @static
15879          */
15880         lock: function() { this.locked = true; },
15881
15882         /**
15883          * Unlock all drag and drop functionality
15884          * @method unlock
15885          * @static
15886          */
15887         unlock: function() { this.locked = false; },
15888
15889         /**
15890          * Is drag and drop locked?
15891          * @method isLocked
15892          * @return {boolean} True if drag and drop is locked, false otherwise.
15893          * @static
15894          */
15895         isLocked: function() { return this.locked; },
15896
15897         /**
15898          * Location cache that is set for all drag drop objects when a drag is
15899          * initiated, cleared when the drag is finished.
15900          * @property locationCache
15901          * @private
15902          * @static
15903          */
15904         locationCache: {},
15905
15906         /**
15907          * Set useCache to false if you want to force object the lookup of each
15908          * drag and drop linked element constantly during a drag.
15909          * @property useCache
15910          * @type boolean
15911          * @static
15912          */
15913         useCache: true,
15914
15915         /**
15916          * The number of pixels that the mouse needs to move after the
15917          * mousedown before the drag is initiated.  Default=3;
15918          * @property clickPixelThresh
15919          * @type int
15920          * @static
15921          */
15922         clickPixelThresh: 3,
15923
15924         /**
15925          * The number of milliseconds after the mousedown event to initiate the
15926          * drag if we don't get a mouseup event. Default=1000
15927          * @property clickTimeThresh
15928          * @type int
15929          * @static
15930          */
15931         clickTimeThresh: 350,
15932
15933         /**
15934          * Flag that indicates that either the drag pixel threshold or the
15935          * mousdown time threshold has been met
15936          * @property dragThreshMet
15937          * @type boolean
15938          * @private
15939          * @static
15940          */
15941         dragThreshMet: false,
15942
15943         /**
15944          * Timeout used for the click time threshold
15945          * @property clickTimeout
15946          * @type Object
15947          * @private
15948          * @static
15949          */
15950         clickTimeout: null,
15951
15952         /**
15953          * The X position of the mousedown event stored for later use when a
15954          * drag threshold is met.
15955          * @property startX
15956          * @type int
15957          * @private
15958          * @static
15959          */
15960         startX: 0,
15961
15962         /**
15963          * The Y position of the mousedown event stored for later use when a
15964          * drag threshold is met.
15965          * @property startY
15966          * @type int
15967          * @private
15968          * @static
15969          */
15970         startY: 0,
15971
15972         /**
15973          * Each DragDrop instance must be registered with the DragDropMgr.
15974          * This is executed in DragDrop.init()
15975          * @method regDragDrop
15976          * @param {DragDrop} oDD the DragDrop object to register
15977          * @param {String} sGroup the name of the group this element belongs to
15978          * @static
15979          */
15980         regDragDrop: function(oDD, sGroup) {
15981             if (!this.initialized) { this.init(); }
15982
15983             if (!this.ids[sGroup]) {
15984                 this.ids[sGroup] = {};
15985             }
15986             this.ids[sGroup][oDD.id] = oDD;
15987         },
15988
15989         /**
15990          * Removes the supplied dd instance from the supplied group. Executed
15991          * by DragDrop.removeFromGroup, so don't call this function directly.
15992          * @method removeDDFromGroup
15993          * @private
15994          * @static
15995          */
15996         removeDDFromGroup: function(oDD, sGroup) {
15997             if (!this.ids[sGroup]) {
15998                 this.ids[sGroup] = {};
15999             }
16000
16001             var obj = this.ids[sGroup];
16002             if (obj && obj[oDD.id]) {
16003                 delete obj[oDD.id];
16004             }
16005         },
16006
16007         /**
16008          * Unregisters a drag and drop item.  This is executed in
16009          * DragDrop.unreg, use that method instead of calling this directly.
16010          * @method _remove
16011          * @private
16012          * @static
16013          */
16014         _remove: function(oDD) {
16015             for (var g in oDD.groups) {
16016                 if (g && this.ids[g][oDD.id]) {
16017                     delete this.ids[g][oDD.id];
16018                 }
16019             }
16020             delete this.handleIds[oDD.id];
16021         },
16022
16023         /**
16024          * Each DragDrop handle element must be registered.  This is done
16025          * automatically when executing DragDrop.setHandleElId()
16026          * @method regHandle
16027          * @param {String} sDDId the DragDrop id this element is a handle for
16028          * @param {String} sHandleId the id of the element that is the drag
16029          * handle
16030          * @static
16031          */
16032         regHandle: function(sDDId, sHandleId) {
16033             if (!this.handleIds[sDDId]) {
16034                 this.handleIds[sDDId] = {};
16035             }
16036             this.handleIds[sDDId][sHandleId] = sHandleId;
16037         },
16038
16039         /**
16040          * Utility function to determine if a given element has been
16041          * registered as a drag drop item.
16042          * @method isDragDrop
16043          * @param {String} id the element id to check
16044          * @return {boolean} true if this element is a DragDrop item,
16045          * false otherwise
16046          * @static
16047          */
16048         isDragDrop: function(id) {
16049             return ( this.getDDById(id) ) ? true : false;
16050         },
16051
16052         /**
16053          * Returns the drag and drop instances that are in all groups the
16054          * passed in instance belongs to.
16055          * @method getRelated
16056          * @param {DragDrop} p_oDD the obj to get related data for
16057          * @param {boolean} bTargetsOnly if true, only return targetable objs
16058          * @return {DragDrop[]} the related instances
16059          * @static
16060          */
16061         getRelated: function(p_oDD, bTargetsOnly) {
16062             var oDDs = [];
16063             for (var i in p_oDD.groups) {
16064                 for (j in this.ids[i]) {
16065                     var dd = this.ids[i][j];
16066                     if (! this.isTypeOfDD(dd)) {
16067                         continue;
16068                     }
16069                     if (!bTargetsOnly || dd.isTarget) {
16070                         oDDs[oDDs.length] = dd;
16071                     }
16072                 }
16073             }
16074
16075             return oDDs;
16076         },
16077
16078         /**
16079          * Returns true if the specified dd target is a legal target for
16080          * the specifice drag obj
16081          * @method isLegalTarget
16082          * @param {DragDrop} the drag obj
16083          * @param {DragDrop} the target
16084          * @return {boolean} true if the target is a legal target for the
16085          * dd obj
16086          * @static
16087          */
16088         isLegalTarget: function (oDD, oTargetDD) {
16089             var targets = this.getRelated(oDD, true);
16090             for (var i=0, len=targets.length;i<len;++i) {
16091                 if (targets[i].id == oTargetDD.id) {
16092                     return true;
16093                 }
16094             }
16095
16096             return false;
16097         },
16098
16099         /**
16100          * My goal is to be able to transparently determine if an object is
16101          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16102          * returns "object", oDD.constructor.toString() always returns
16103          * "DragDrop" and not the name of the subclass.  So for now it just
16104          * evaluates a well-known variable in DragDrop.
16105          * @method isTypeOfDD
16106          * @param {Object} the object to evaluate
16107          * @return {boolean} true if typeof oDD = DragDrop
16108          * @static
16109          */
16110         isTypeOfDD: function (oDD) {
16111             return (oDD && oDD.__ygDragDrop);
16112         },
16113
16114         /**
16115          * Utility function to determine if a given element has been
16116          * registered as a drag drop handle for the given Drag Drop object.
16117          * @method isHandle
16118          * @param {String} id the element id to check
16119          * @return {boolean} true if this element is a DragDrop handle, false
16120          * otherwise
16121          * @static
16122          */
16123         isHandle: function(sDDId, sHandleId) {
16124             return ( this.handleIds[sDDId] &&
16125                             this.handleIds[sDDId][sHandleId] );
16126         },
16127
16128         /**
16129          * Returns the DragDrop instance for a given id
16130          * @method getDDById
16131          * @param {String} id the id of the DragDrop object
16132          * @return {DragDrop} the drag drop object, null if it is not found
16133          * @static
16134          */
16135         getDDById: function(id) {
16136             for (var i in this.ids) {
16137                 if (this.ids[i][id]) {
16138                     return this.ids[i][id];
16139                 }
16140             }
16141             return null;
16142         },
16143
16144         /**
16145          * Fired after a registered DragDrop object gets the mousedown event.
16146          * Sets up the events required to track the object being dragged
16147          * @method handleMouseDown
16148          * @param {Event} e the event
16149          * @param oDD the DragDrop object being dragged
16150          * @private
16151          * @static
16152          */
16153         handleMouseDown: function(e, oDD) {
16154             if(Roo.QuickTips){
16155                 Roo.QuickTips.disable();
16156             }
16157             this.currentTarget = e.getTarget();
16158
16159             this.dragCurrent = oDD;
16160
16161             var el = oDD.getEl();
16162
16163             // track start position
16164             this.startX = e.getPageX();
16165             this.startY = e.getPageY();
16166
16167             this.deltaX = this.startX - el.offsetLeft;
16168             this.deltaY = this.startY - el.offsetTop;
16169
16170             this.dragThreshMet = false;
16171
16172             this.clickTimeout = setTimeout(
16173                     function() {
16174                         var DDM = Roo.dd.DDM;
16175                         DDM.startDrag(DDM.startX, DDM.startY);
16176                     },
16177                     this.clickTimeThresh );
16178         },
16179
16180         /**
16181          * Fired when either the drag pixel threshol or the mousedown hold
16182          * time threshold has been met.
16183          * @method startDrag
16184          * @param x {int} the X position of the original mousedown
16185          * @param y {int} the Y position of the original mousedown
16186          * @static
16187          */
16188         startDrag: function(x, y) {
16189             clearTimeout(this.clickTimeout);
16190             if (this.dragCurrent) {
16191                 this.dragCurrent.b4StartDrag(x, y);
16192                 this.dragCurrent.startDrag(x, y);
16193             }
16194             this.dragThreshMet = true;
16195         },
16196
16197         /**
16198          * Internal function to handle the mouseup event.  Will be invoked
16199          * from the context of the document.
16200          * @method handleMouseUp
16201          * @param {Event} e the event
16202          * @private
16203          * @static
16204          */
16205         handleMouseUp: function(e) {
16206
16207             if(Roo.QuickTips){
16208                 Roo.QuickTips.enable();
16209             }
16210             if (! this.dragCurrent) {
16211                 return;
16212             }
16213
16214             clearTimeout(this.clickTimeout);
16215
16216             if (this.dragThreshMet) {
16217                 this.fireEvents(e, true);
16218             } else {
16219             }
16220
16221             this.stopDrag(e);
16222
16223             this.stopEvent(e);
16224         },
16225
16226         /**
16227          * Utility to stop event propagation and event default, if these
16228          * features are turned on.
16229          * @method stopEvent
16230          * @param {Event} e the event as returned by this.getEvent()
16231          * @static
16232          */
16233         stopEvent: function(e){
16234             if(this.stopPropagation) {
16235                 e.stopPropagation();
16236             }
16237
16238             if (this.preventDefault) {
16239                 e.preventDefault();
16240             }
16241         },
16242
16243         /**
16244          * Internal function to clean up event handlers after the drag
16245          * operation is complete
16246          * @method stopDrag
16247          * @param {Event} e the event
16248          * @private
16249          * @static
16250          */
16251         stopDrag: function(e) {
16252             // Fire the drag end event for the item that was dragged
16253             if (this.dragCurrent) {
16254                 if (this.dragThreshMet) {
16255                     this.dragCurrent.b4EndDrag(e);
16256                     this.dragCurrent.endDrag(e);
16257                 }
16258
16259                 this.dragCurrent.onMouseUp(e);
16260             }
16261
16262             this.dragCurrent = null;
16263             this.dragOvers = {};
16264         },
16265
16266         /**
16267          * Internal function to handle the mousemove event.  Will be invoked
16268          * from the context of the html element.
16269          *
16270          * @TODO figure out what we can do about mouse events lost when the
16271          * user drags objects beyond the window boundary.  Currently we can
16272          * detect this in internet explorer by verifying that the mouse is
16273          * down during the mousemove event.  Firefox doesn't give us the
16274          * button state on the mousemove event.
16275          * @method handleMouseMove
16276          * @param {Event} e the event
16277          * @private
16278          * @static
16279          */
16280         handleMouseMove: function(e) {
16281             if (! this.dragCurrent) {
16282                 return true;
16283             }
16284
16285             // var button = e.which || e.button;
16286
16287             // check for IE mouseup outside of page boundary
16288             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16289                 this.stopEvent(e);
16290                 return this.handleMouseUp(e);
16291             }
16292
16293             if (!this.dragThreshMet) {
16294                 var diffX = Math.abs(this.startX - e.getPageX());
16295                 var diffY = Math.abs(this.startY - e.getPageY());
16296                 if (diffX > this.clickPixelThresh ||
16297                             diffY > this.clickPixelThresh) {
16298                     this.startDrag(this.startX, this.startY);
16299                 }
16300             }
16301
16302             if (this.dragThreshMet) {
16303                 this.dragCurrent.b4Drag(e);
16304                 this.dragCurrent.onDrag(e);
16305                 if(!this.dragCurrent.moveOnly){
16306                     this.fireEvents(e, false);
16307                 }
16308             }
16309
16310             this.stopEvent(e);
16311
16312             return true;
16313         },
16314
16315         /**
16316          * Iterates over all of the DragDrop elements to find ones we are
16317          * hovering over or dropping on
16318          * @method fireEvents
16319          * @param {Event} e the event
16320          * @param {boolean} isDrop is this a drop op or a mouseover op?
16321          * @private
16322          * @static
16323          */
16324         fireEvents: function(e, isDrop) {
16325             var dc = this.dragCurrent;
16326
16327             // If the user did the mouse up outside of the window, we could
16328             // get here even though we have ended the drag.
16329             if (!dc || dc.isLocked()) {
16330                 return;
16331             }
16332
16333             var pt = e.getPoint();
16334
16335             // cache the previous dragOver array
16336             var oldOvers = [];
16337
16338             var outEvts   = [];
16339             var overEvts  = [];
16340             var dropEvts  = [];
16341             var enterEvts = [];
16342
16343             // Check to see if the object(s) we were hovering over is no longer
16344             // being hovered over so we can fire the onDragOut event
16345             for (var i in this.dragOvers) {
16346
16347                 var ddo = this.dragOvers[i];
16348
16349                 if (! this.isTypeOfDD(ddo)) {
16350                     continue;
16351                 }
16352
16353                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16354                     outEvts.push( ddo );
16355                 }
16356
16357                 oldOvers[i] = true;
16358                 delete this.dragOvers[i];
16359             }
16360
16361             for (var sGroup in dc.groups) {
16362
16363                 if ("string" != typeof sGroup) {
16364                     continue;
16365                 }
16366
16367                 for (i in this.ids[sGroup]) {
16368                     var oDD = this.ids[sGroup][i];
16369                     if (! this.isTypeOfDD(oDD)) {
16370                         continue;
16371                     }
16372
16373                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16374                         if (this.isOverTarget(pt, oDD, this.mode)) {
16375                             // look for drop interactions
16376                             if (isDrop) {
16377                                 dropEvts.push( oDD );
16378                             // look for drag enter and drag over interactions
16379                             } else {
16380
16381                                 // initial drag over: dragEnter fires
16382                                 if (!oldOvers[oDD.id]) {
16383                                     enterEvts.push( oDD );
16384                                 // subsequent drag overs: dragOver fires
16385                                 } else {
16386                                     overEvts.push( oDD );
16387                                 }
16388
16389                                 this.dragOvers[oDD.id] = oDD;
16390                             }
16391                         }
16392                     }
16393                 }
16394             }
16395
16396             if (this.mode) {
16397                 if (outEvts.length) {
16398                     dc.b4DragOut(e, outEvts);
16399                     dc.onDragOut(e, outEvts);
16400                 }
16401
16402                 if (enterEvts.length) {
16403                     dc.onDragEnter(e, enterEvts);
16404                 }
16405
16406                 if (overEvts.length) {
16407                     dc.b4DragOver(e, overEvts);
16408                     dc.onDragOver(e, overEvts);
16409                 }
16410
16411                 if (dropEvts.length) {
16412                     dc.b4DragDrop(e, dropEvts);
16413                     dc.onDragDrop(e, dropEvts);
16414                 }
16415
16416             } else {
16417                 // fire dragout events
16418                 var len = 0;
16419                 for (i=0, len=outEvts.length; i<len; ++i) {
16420                     dc.b4DragOut(e, outEvts[i].id);
16421                     dc.onDragOut(e, outEvts[i].id);
16422                 }
16423
16424                 // fire enter events
16425                 for (i=0,len=enterEvts.length; i<len; ++i) {
16426                     // dc.b4DragEnter(e, oDD.id);
16427                     dc.onDragEnter(e, enterEvts[i].id);
16428                 }
16429
16430                 // fire over events
16431                 for (i=0,len=overEvts.length; i<len; ++i) {
16432                     dc.b4DragOver(e, overEvts[i].id);
16433                     dc.onDragOver(e, overEvts[i].id);
16434                 }
16435
16436                 // fire drop events
16437                 for (i=0, len=dropEvts.length; i<len; ++i) {
16438                     dc.b4DragDrop(e, dropEvts[i].id);
16439                     dc.onDragDrop(e, dropEvts[i].id);
16440                 }
16441
16442             }
16443
16444             // notify about a drop that did not find a target
16445             if (isDrop && !dropEvts.length) {
16446                 dc.onInvalidDrop(e);
16447             }
16448
16449         },
16450
16451         /**
16452          * Helper function for getting the best match from the list of drag
16453          * and drop objects returned by the drag and drop events when we are
16454          * in INTERSECT mode.  It returns either the first object that the
16455          * cursor is over, or the object that has the greatest overlap with
16456          * the dragged element.
16457          * @method getBestMatch
16458          * @param  {DragDrop[]} dds The array of drag and drop objects
16459          * targeted
16460          * @return {DragDrop}       The best single match
16461          * @static
16462          */
16463         getBestMatch: function(dds) {
16464             var winner = null;
16465             // Return null if the input is not what we expect
16466             //if (!dds || !dds.length || dds.length == 0) {
16467                // winner = null;
16468             // If there is only one item, it wins
16469             //} else if (dds.length == 1) {
16470
16471             var len = dds.length;
16472
16473             if (len == 1) {
16474                 winner = dds[0];
16475             } else {
16476                 // Loop through the targeted items
16477                 for (var i=0; i<len; ++i) {
16478                     var dd = dds[i];
16479                     // If the cursor is over the object, it wins.  If the
16480                     // cursor is over multiple matches, the first one we come
16481                     // to wins.
16482                     if (dd.cursorIsOver) {
16483                         winner = dd;
16484                         break;
16485                     // Otherwise the object with the most overlap wins
16486                     } else {
16487                         if (!winner ||
16488                             winner.overlap.getArea() < dd.overlap.getArea()) {
16489                             winner = dd;
16490                         }
16491                     }
16492                 }
16493             }
16494
16495             return winner;
16496         },
16497
16498         /**
16499          * Refreshes the cache of the top-left and bottom-right points of the
16500          * drag and drop objects in the specified group(s).  This is in the
16501          * format that is stored in the drag and drop instance, so typical
16502          * usage is:
16503          * <code>
16504          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16505          * </code>
16506          * Alternatively:
16507          * <code>
16508          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16509          * </code>
16510          * @TODO this really should be an indexed array.  Alternatively this
16511          * method could accept both.
16512          * @method refreshCache
16513          * @param {Object} groups an associative array of groups to refresh
16514          * @static
16515          */
16516         refreshCache: function(groups) {
16517             for (var sGroup in groups) {
16518                 if ("string" != typeof sGroup) {
16519                     continue;
16520                 }
16521                 for (var i in this.ids[sGroup]) {
16522                     var oDD = this.ids[sGroup][i];
16523
16524                     if (this.isTypeOfDD(oDD)) {
16525                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16526                         var loc = this.getLocation(oDD);
16527                         if (loc) {
16528                             this.locationCache[oDD.id] = loc;
16529                         } else {
16530                             delete this.locationCache[oDD.id];
16531                             // this will unregister the drag and drop object if
16532                             // the element is not in a usable state
16533                             // oDD.unreg();
16534                         }
16535                     }
16536                 }
16537             }
16538         },
16539
16540         /**
16541          * This checks to make sure an element exists and is in the DOM.  The
16542          * main purpose is to handle cases where innerHTML is used to remove
16543          * drag and drop objects from the DOM.  IE provides an 'unspecified
16544          * error' when trying to access the offsetParent of such an element
16545          * @method verifyEl
16546          * @param {HTMLElement} el the element to check
16547          * @return {boolean} true if the element looks usable
16548          * @static
16549          */
16550         verifyEl: function(el) {
16551             if (el) {
16552                 var parent;
16553                 if(Roo.isIE){
16554                     try{
16555                         parent = el.offsetParent;
16556                     }catch(e){}
16557                 }else{
16558                     parent = el.offsetParent;
16559                 }
16560                 if (parent) {
16561                     return true;
16562                 }
16563             }
16564
16565             return false;
16566         },
16567
16568         /**
16569          * Returns a Region object containing the drag and drop element's position
16570          * and size, including the padding configured for it
16571          * @method getLocation
16572          * @param {DragDrop} oDD the drag and drop object to get the
16573          *                       location for
16574          * @return {Roo.lib.Region} a Region object representing the total area
16575          *                             the element occupies, including any padding
16576          *                             the instance is configured for.
16577          * @static
16578          */
16579         getLocation: function(oDD) {
16580             if (! this.isTypeOfDD(oDD)) {
16581                 return null;
16582             }
16583
16584             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16585
16586             try {
16587                 pos= Roo.lib.Dom.getXY(el);
16588             } catch (e) { }
16589
16590             if (!pos) {
16591                 return null;
16592             }
16593
16594             x1 = pos[0];
16595             x2 = x1 + el.offsetWidth;
16596             y1 = pos[1];
16597             y2 = y1 + el.offsetHeight;
16598
16599             t = y1 - oDD.padding[0];
16600             r = x2 + oDD.padding[1];
16601             b = y2 + oDD.padding[2];
16602             l = x1 - oDD.padding[3];
16603
16604             return new Roo.lib.Region( t, r, b, l );
16605         },
16606
16607         /**
16608          * Checks the cursor location to see if it over the target
16609          * @method isOverTarget
16610          * @param {Roo.lib.Point} pt The point to evaluate
16611          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16612          * @return {boolean} true if the mouse is over the target
16613          * @private
16614          * @static
16615          */
16616         isOverTarget: function(pt, oTarget, intersect) {
16617             // use cache if available
16618             var loc = this.locationCache[oTarget.id];
16619             if (!loc || !this.useCache) {
16620                 loc = this.getLocation(oTarget);
16621                 this.locationCache[oTarget.id] = loc;
16622
16623             }
16624
16625             if (!loc) {
16626                 return false;
16627             }
16628
16629             oTarget.cursorIsOver = loc.contains( pt );
16630
16631             // DragDrop is using this as a sanity check for the initial mousedown
16632             // in this case we are done.  In POINT mode, if the drag obj has no
16633             // contraints, we are also done. Otherwise we need to evaluate the
16634             // location of the target as related to the actual location of the
16635             // dragged element.
16636             var dc = this.dragCurrent;
16637             if (!dc || !dc.getTargetCoord ||
16638                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16639                 return oTarget.cursorIsOver;
16640             }
16641
16642             oTarget.overlap = null;
16643
16644             // Get the current location of the drag element, this is the
16645             // location of the mouse event less the delta that represents
16646             // where the original mousedown happened on the element.  We
16647             // need to consider constraints and ticks as well.
16648             var pos = dc.getTargetCoord(pt.x, pt.y);
16649
16650             var el = dc.getDragEl();
16651             var curRegion = new Roo.lib.Region( pos.y,
16652                                                    pos.x + el.offsetWidth,
16653                                                    pos.y + el.offsetHeight,
16654                                                    pos.x );
16655
16656             var overlap = curRegion.intersect(loc);
16657
16658             if (overlap) {
16659                 oTarget.overlap = overlap;
16660                 return (intersect) ? true : oTarget.cursorIsOver;
16661             } else {
16662                 return false;
16663             }
16664         },
16665
16666         /**
16667          * unload event handler
16668          * @method _onUnload
16669          * @private
16670          * @static
16671          */
16672         _onUnload: function(e, me) {
16673             Roo.dd.DragDropMgr.unregAll();
16674         },
16675
16676         /**
16677          * Cleans up the drag and drop events and objects.
16678          * @method unregAll
16679          * @private
16680          * @static
16681          */
16682         unregAll: function() {
16683
16684             if (this.dragCurrent) {
16685                 this.stopDrag();
16686                 this.dragCurrent = null;
16687             }
16688
16689             this._execOnAll("unreg", []);
16690
16691             for (i in this.elementCache) {
16692                 delete this.elementCache[i];
16693             }
16694
16695             this.elementCache = {};
16696             this.ids = {};
16697         },
16698
16699         /**
16700          * A cache of DOM elements
16701          * @property elementCache
16702          * @private
16703          * @static
16704          */
16705         elementCache: {},
16706
16707         /**
16708          * Get the wrapper for the DOM element specified
16709          * @method getElWrapper
16710          * @param {String} id the id of the element to get
16711          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16712          * @private
16713          * @deprecated This wrapper isn't that useful
16714          * @static
16715          */
16716         getElWrapper: function(id) {
16717             var oWrapper = this.elementCache[id];
16718             if (!oWrapper || !oWrapper.el) {
16719                 oWrapper = this.elementCache[id] =
16720                     new this.ElementWrapper(Roo.getDom(id));
16721             }
16722             return oWrapper;
16723         },
16724
16725         /**
16726          * Returns the actual DOM element
16727          * @method getElement
16728          * @param {String} id the id of the elment to get
16729          * @return {Object} The element
16730          * @deprecated use Roo.getDom instead
16731          * @static
16732          */
16733         getElement: function(id) {
16734             return Roo.getDom(id);
16735         },
16736
16737         /**
16738          * Returns the style property for the DOM element (i.e.,
16739          * document.getElById(id).style)
16740          * @method getCss
16741          * @param {String} id the id of the elment to get
16742          * @return {Object} The style property of the element
16743          * @deprecated use Roo.getDom instead
16744          * @static
16745          */
16746         getCss: function(id) {
16747             var el = Roo.getDom(id);
16748             return (el) ? el.style : null;
16749         },
16750
16751         /**
16752          * Inner class for cached elements
16753          * @class DragDropMgr.ElementWrapper
16754          * @for DragDropMgr
16755          * @private
16756          * @deprecated
16757          */
16758         ElementWrapper: function(el) {
16759                 /**
16760                  * The element
16761                  * @property el
16762                  */
16763                 this.el = el || null;
16764                 /**
16765                  * The element id
16766                  * @property id
16767                  */
16768                 this.id = this.el && el.id;
16769                 /**
16770                  * A reference to the style property
16771                  * @property css
16772                  */
16773                 this.css = this.el && el.style;
16774             },
16775
16776         /**
16777          * Returns the X position of an html element
16778          * @method getPosX
16779          * @param el the element for which to get the position
16780          * @return {int} the X coordinate
16781          * @for DragDropMgr
16782          * @deprecated use Roo.lib.Dom.getX instead
16783          * @static
16784          */
16785         getPosX: function(el) {
16786             return Roo.lib.Dom.getX(el);
16787         },
16788
16789         /**
16790          * Returns the Y position of an html element
16791          * @method getPosY
16792          * @param el the element for which to get the position
16793          * @return {int} the Y coordinate
16794          * @deprecated use Roo.lib.Dom.getY instead
16795          * @static
16796          */
16797         getPosY: function(el) {
16798             return Roo.lib.Dom.getY(el);
16799         },
16800
16801         /**
16802          * Swap two nodes.  In IE, we use the native method, for others we
16803          * emulate the IE behavior
16804          * @method swapNode
16805          * @param n1 the first node to swap
16806          * @param n2 the other node to swap
16807          * @static
16808          */
16809         swapNode: function(n1, n2) {
16810             if (n1.swapNode) {
16811                 n1.swapNode(n2);
16812             } else {
16813                 var p = n2.parentNode;
16814                 var s = n2.nextSibling;
16815
16816                 if (s == n1) {
16817                     p.insertBefore(n1, n2);
16818                 } else if (n2 == n1.nextSibling) {
16819                     p.insertBefore(n2, n1);
16820                 } else {
16821                     n1.parentNode.replaceChild(n2, n1);
16822                     p.insertBefore(n1, s);
16823                 }
16824             }
16825         },
16826
16827         /**
16828          * Returns the current scroll position
16829          * @method getScroll
16830          * @private
16831          * @static
16832          */
16833         getScroll: function () {
16834             var t, l, dde=document.documentElement, db=document.body;
16835             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16836                 t = dde.scrollTop;
16837                 l = dde.scrollLeft;
16838             } else if (db) {
16839                 t = db.scrollTop;
16840                 l = db.scrollLeft;
16841             } else {
16842
16843             }
16844             return { top: t, left: l };
16845         },
16846
16847         /**
16848          * Returns the specified element style property
16849          * @method getStyle
16850          * @param {HTMLElement} el          the element
16851          * @param {string}      styleProp   the style property
16852          * @return {string} The value of the style property
16853          * @deprecated use Roo.lib.Dom.getStyle
16854          * @static
16855          */
16856         getStyle: function(el, styleProp) {
16857             return Roo.fly(el).getStyle(styleProp);
16858         },
16859
16860         /**
16861          * Gets the scrollTop
16862          * @method getScrollTop
16863          * @return {int} the document's scrollTop
16864          * @static
16865          */
16866         getScrollTop: function () { return this.getScroll().top; },
16867
16868         /**
16869          * Gets the scrollLeft
16870          * @method getScrollLeft
16871          * @return {int} the document's scrollTop
16872          * @static
16873          */
16874         getScrollLeft: function () { return this.getScroll().left; },
16875
16876         /**
16877          * Sets the x/y position of an element to the location of the
16878          * target element.
16879          * @method moveToEl
16880          * @param {HTMLElement} moveEl      The element to move
16881          * @param {HTMLElement} targetEl    The position reference element
16882          * @static
16883          */
16884         moveToEl: function (moveEl, targetEl) {
16885             var aCoord = Roo.lib.Dom.getXY(targetEl);
16886             Roo.lib.Dom.setXY(moveEl, aCoord);
16887         },
16888
16889         /**
16890          * Numeric array sort function
16891          * @method numericSort
16892          * @static
16893          */
16894         numericSort: function(a, b) { return (a - b); },
16895
16896         /**
16897          * Internal counter
16898          * @property _timeoutCount
16899          * @private
16900          * @static
16901          */
16902         _timeoutCount: 0,
16903
16904         /**
16905          * Trying to make the load order less important.  Without this we get
16906          * an error if this file is loaded before the Event Utility.
16907          * @method _addListeners
16908          * @private
16909          * @static
16910          */
16911         _addListeners: function() {
16912             var DDM = Roo.dd.DDM;
16913             if ( Roo.lib.Event && document ) {
16914                 DDM._onLoad();
16915             } else {
16916                 if (DDM._timeoutCount > 2000) {
16917                 } else {
16918                     setTimeout(DDM._addListeners, 10);
16919                     if (document && document.body) {
16920                         DDM._timeoutCount += 1;
16921                     }
16922                 }
16923             }
16924         },
16925
16926         /**
16927          * Recursively searches the immediate parent and all child nodes for
16928          * the handle element in order to determine wheter or not it was
16929          * clicked.
16930          * @method handleWasClicked
16931          * @param node the html element to inspect
16932          * @static
16933          */
16934         handleWasClicked: function(node, id) {
16935             if (this.isHandle(id, node.id)) {
16936                 return true;
16937             } else {
16938                 // check to see if this is a text node child of the one we want
16939                 var p = node.parentNode;
16940
16941                 while (p) {
16942                     if (this.isHandle(id, p.id)) {
16943                         return true;
16944                     } else {
16945                         p = p.parentNode;
16946                     }
16947                 }
16948             }
16949
16950             return false;
16951         }
16952
16953     };
16954
16955 }();
16956
16957 // shorter alias, save a few bytes
16958 Roo.dd.DDM = Roo.dd.DragDropMgr;
16959 Roo.dd.DDM._addListeners();
16960
16961 }/*
16962  * Based on:
16963  * Ext JS Library 1.1.1
16964  * Copyright(c) 2006-2007, Ext JS, LLC.
16965  *
16966  * Originally Released Under LGPL - original licence link has changed is not relivant.
16967  *
16968  * Fork - LGPL
16969  * <script type="text/javascript">
16970  */
16971
16972 /**
16973  * @class Roo.dd.DD
16974  * A DragDrop implementation where the linked element follows the
16975  * mouse cursor during a drag.
16976  * @extends Roo.dd.DragDrop
16977  * @constructor
16978  * @param {String} id the id of the linked element
16979  * @param {String} sGroup the group of related DragDrop items
16980  * @param {object} config an object containing configurable attributes
16981  *                Valid properties for DD:
16982  *                    scroll
16983  */
16984 Roo.dd.DD = function(id, sGroup, config) {
16985     if (id) {
16986         this.init(id, sGroup, config);
16987     }
16988 };
16989
16990 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16991
16992     /**
16993      * When set to true, the utility automatically tries to scroll the browser
16994      * window wehn a drag and drop element is dragged near the viewport boundary.
16995      * Defaults to true.
16996      * @property scroll
16997      * @type boolean
16998      */
16999     scroll: true,
17000
17001     /**
17002      * Sets the pointer offset to the distance between the linked element's top
17003      * left corner and the location the element was clicked
17004      * @method autoOffset
17005      * @param {int} iPageX the X coordinate of the click
17006      * @param {int} iPageY the Y coordinate of the click
17007      */
17008     autoOffset: function(iPageX, iPageY) {
17009         var x = iPageX - this.startPageX;
17010         var y = iPageY - this.startPageY;
17011         this.setDelta(x, y);
17012     },
17013
17014     /**
17015      * Sets the pointer offset.  You can call this directly to force the
17016      * offset to be in a particular location (e.g., pass in 0,0 to set it
17017      * to the center of the object)
17018      * @method setDelta
17019      * @param {int} iDeltaX the distance from the left
17020      * @param {int} iDeltaY the distance from the top
17021      */
17022     setDelta: function(iDeltaX, iDeltaY) {
17023         this.deltaX = iDeltaX;
17024         this.deltaY = iDeltaY;
17025     },
17026
17027     /**
17028      * Sets the drag element to the location of the mousedown or click event,
17029      * maintaining the cursor location relative to the location on the element
17030      * that was clicked.  Override this if you want to place the element in a
17031      * location other than where the cursor is.
17032      * @method setDragElPos
17033      * @param {int} iPageX the X coordinate of the mousedown or drag event
17034      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17035      */
17036     setDragElPos: function(iPageX, iPageY) {
17037         // the first time we do this, we are going to check to make sure
17038         // the element has css positioning
17039
17040         var el = this.getDragEl();
17041         this.alignElWithMouse(el, iPageX, iPageY);
17042     },
17043
17044     /**
17045      * Sets the element to the location of the mousedown or click event,
17046      * maintaining the cursor location relative to the location on the element
17047      * that was clicked.  Override this if you want to place the element in a
17048      * location other than where the cursor is.
17049      * @method alignElWithMouse
17050      * @param {HTMLElement} el the element to move
17051      * @param {int} iPageX the X coordinate of the mousedown or drag event
17052      * @param {int} iPageY the Y coordinate of the mousedown or drag event
17053      */
17054     alignElWithMouse: function(el, iPageX, iPageY) {
17055         var oCoord = this.getTargetCoord(iPageX, iPageY);
17056         var fly = el.dom ? el : Roo.fly(el);
17057         if (!this.deltaSetXY) {
17058             var aCoord = [oCoord.x, oCoord.y];
17059             fly.setXY(aCoord);
17060             var newLeft = fly.getLeft(true);
17061             var newTop  = fly.getTop(true);
17062             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
17063         } else {
17064             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
17065         }
17066
17067         this.cachePosition(oCoord.x, oCoord.y);
17068         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
17069         return oCoord;
17070     },
17071
17072     /**
17073      * Saves the most recent position so that we can reset the constraints and
17074      * tick marks on-demand.  We need to know this so that we can calculate the
17075      * number of pixels the element is offset from its original position.
17076      * @method cachePosition
17077      * @param iPageX the current x position (optional, this just makes it so we
17078      * don't have to look it up again)
17079      * @param iPageY the current y position (optional, this just makes it so we
17080      * don't have to look it up again)
17081      */
17082     cachePosition: function(iPageX, iPageY) {
17083         if (iPageX) {
17084             this.lastPageX = iPageX;
17085             this.lastPageY = iPageY;
17086         } else {
17087             var aCoord = Roo.lib.Dom.getXY(this.getEl());
17088             this.lastPageX = aCoord[0];
17089             this.lastPageY = aCoord[1];
17090         }
17091     },
17092
17093     /**
17094      * Auto-scroll the window if the dragged object has been moved beyond the
17095      * visible window boundary.
17096      * @method autoScroll
17097      * @param {int} x the drag element's x position
17098      * @param {int} y the drag element's y position
17099      * @param {int} h the height of the drag element
17100      * @param {int} w the width of the drag element
17101      * @private
17102      */
17103     autoScroll: function(x, y, h, w) {
17104
17105         if (this.scroll) {
17106             // The client height
17107             var clientH = Roo.lib.Dom.getViewWidth();
17108
17109             // The client width
17110             var clientW = Roo.lib.Dom.getViewHeight();
17111
17112             // The amt scrolled down
17113             var st = this.DDM.getScrollTop();
17114
17115             // The amt scrolled right
17116             var sl = this.DDM.getScrollLeft();
17117
17118             // Location of the bottom of the element
17119             var bot = h + y;
17120
17121             // Location of the right of the element
17122             var right = w + x;
17123
17124             // The distance from the cursor to the bottom of the visible area,
17125             // adjusted so that we don't scroll if the cursor is beyond the
17126             // element drag constraints
17127             var toBot = (clientH + st - y - this.deltaY);
17128
17129             // The distance from the cursor to the right of the visible area
17130             var toRight = (clientW + sl - x - this.deltaX);
17131
17132
17133             // How close to the edge the cursor must be before we scroll
17134             // var thresh = (document.all) ? 100 : 40;
17135             var thresh = 40;
17136
17137             // How many pixels to scroll per autoscroll op.  This helps to reduce
17138             // clunky scrolling. IE is more sensitive about this ... it needs this
17139             // value to be higher.
17140             var scrAmt = (document.all) ? 80 : 30;
17141
17142             // Scroll down if we are near the bottom of the visible page and the
17143             // obj extends below the crease
17144             if ( bot > clientH && toBot < thresh ) {
17145                 window.scrollTo(sl, st + scrAmt);
17146             }
17147
17148             // Scroll up if the window is scrolled down and the top of the object
17149             // goes above the top border
17150             if ( y < st && st > 0 && y - st < thresh ) {
17151                 window.scrollTo(sl, st - scrAmt);
17152             }
17153
17154             // Scroll right if the obj is beyond the right border and the cursor is
17155             // near the border.
17156             if ( right > clientW && toRight < thresh ) {
17157                 window.scrollTo(sl + scrAmt, st);
17158             }
17159
17160             // Scroll left if the window has been scrolled to the right and the obj
17161             // extends past the left border
17162             if ( x < sl && sl > 0 && x - sl < thresh ) {
17163                 window.scrollTo(sl - scrAmt, st);
17164             }
17165         }
17166     },
17167
17168     /**
17169      * Finds the location the element should be placed if we want to move
17170      * it to where the mouse location less the click offset would place us.
17171      * @method getTargetCoord
17172      * @param {int} iPageX the X coordinate of the click
17173      * @param {int} iPageY the Y coordinate of the click
17174      * @return an object that contains the coordinates (Object.x and Object.y)
17175      * @private
17176      */
17177     getTargetCoord: function(iPageX, iPageY) {
17178
17179
17180         var x = iPageX - this.deltaX;
17181         var y = iPageY - this.deltaY;
17182
17183         if (this.constrainX) {
17184             if (x < this.minX) { x = this.minX; }
17185             if (x > this.maxX) { x = this.maxX; }
17186         }
17187
17188         if (this.constrainY) {
17189             if (y < this.minY) { y = this.minY; }
17190             if (y > this.maxY) { y = this.maxY; }
17191         }
17192
17193         x = this.getTick(x, this.xTicks);
17194         y = this.getTick(y, this.yTicks);
17195
17196
17197         return {x:x, y:y};
17198     },
17199
17200     /*
17201      * Sets up config options specific to this class. Overrides
17202      * Roo.dd.DragDrop, but all versions of this method through the
17203      * inheritance chain are called
17204      */
17205     applyConfig: function() {
17206         Roo.dd.DD.superclass.applyConfig.call(this);
17207         this.scroll = (this.config.scroll !== false);
17208     },
17209
17210     /*
17211      * Event that fires prior to the onMouseDown event.  Overrides
17212      * Roo.dd.DragDrop.
17213      */
17214     b4MouseDown: function(e) {
17215         // this.resetConstraints();
17216         this.autoOffset(e.getPageX(),
17217                             e.getPageY());
17218     },
17219
17220     /*
17221      * Event that fires prior to the onDrag event.  Overrides
17222      * Roo.dd.DragDrop.
17223      */
17224     b4Drag: function(e) {
17225         this.setDragElPos(e.getPageX(),
17226                             e.getPageY());
17227     },
17228
17229     toString: function() {
17230         return ("DD " + this.id);
17231     }
17232
17233     //////////////////////////////////////////////////////////////////////////
17234     // Debugging ygDragDrop events that can be overridden
17235     //////////////////////////////////////////////////////////////////////////
17236     /*
17237     startDrag: function(x, y) {
17238     },
17239
17240     onDrag: function(e) {
17241     },
17242
17243     onDragEnter: function(e, id) {
17244     },
17245
17246     onDragOver: function(e, id) {
17247     },
17248
17249     onDragOut: function(e, id) {
17250     },
17251
17252     onDragDrop: function(e, id) {
17253     },
17254
17255     endDrag: function(e) {
17256     }
17257
17258     */
17259
17260 });/*
17261  * Based on:
17262  * Ext JS Library 1.1.1
17263  * Copyright(c) 2006-2007, Ext JS, LLC.
17264  *
17265  * Originally Released Under LGPL - original licence link has changed is not relivant.
17266  *
17267  * Fork - LGPL
17268  * <script type="text/javascript">
17269  */
17270
17271 /**
17272  * @class Roo.dd.DDProxy
17273  * A DragDrop implementation that inserts an empty, bordered div into
17274  * the document that follows the cursor during drag operations.  At the time of
17275  * the click, the frame div is resized to the dimensions of the linked html
17276  * element, and moved to the exact location of the linked element.
17277  *
17278  * References to the "frame" element refer to the single proxy element that
17279  * was created to be dragged in place of all DDProxy elements on the
17280  * page.
17281  *
17282  * @extends Roo.dd.DD
17283  * @constructor
17284  * @param {String} id the id of the linked html element
17285  * @param {String} sGroup the group of related DragDrop objects
17286  * @param {object} config an object containing configurable attributes
17287  *                Valid properties for DDProxy in addition to those in DragDrop:
17288  *                   resizeFrame, centerFrame, dragElId
17289  */
17290 Roo.dd.DDProxy = function(id, sGroup, config) {
17291     if (id) {
17292         this.init(id, sGroup, config);
17293         this.initFrame();
17294     }
17295 };
17296
17297 /**
17298  * The default drag frame div id
17299  * @property Roo.dd.DDProxy.dragElId
17300  * @type String
17301  * @static
17302  */
17303 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17304
17305 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17306
17307     /**
17308      * By default we resize the drag frame to be the same size as the element
17309      * we want to drag (this is to get the frame effect).  We can turn it off
17310      * if we want a different behavior.
17311      * @property resizeFrame
17312      * @type boolean
17313      */
17314     resizeFrame: true,
17315
17316     /**
17317      * By default the frame is positioned exactly where the drag element is, so
17318      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17319      * you do not have constraints on the obj is to have the drag frame centered
17320      * around the cursor.  Set centerFrame to true for this effect.
17321      * @property centerFrame
17322      * @type boolean
17323      */
17324     centerFrame: false,
17325
17326     /**
17327      * Creates the proxy element if it does not yet exist
17328      * @method createFrame
17329      */
17330     createFrame: function() {
17331         var self = this;
17332         var body = document.body;
17333
17334         if (!body || !body.firstChild) {
17335             setTimeout( function() { self.createFrame(); }, 50 );
17336             return;
17337         }
17338
17339         var div = this.getDragEl();
17340
17341         if (!div) {
17342             div    = document.createElement("div");
17343             div.id = this.dragElId;
17344             var s  = div.style;
17345
17346             s.position   = "absolute";
17347             s.visibility = "hidden";
17348             s.cursor     = "move";
17349             s.border     = "2px solid #aaa";
17350             s.zIndex     = 999;
17351
17352             // appendChild can blow up IE if invoked prior to the window load event
17353             // while rendering a table.  It is possible there are other scenarios
17354             // that would cause this to happen as well.
17355             body.insertBefore(div, body.firstChild);
17356         }
17357     },
17358
17359     /**
17360      * Initialization for the drag frame element.  Must be called in the
17361      * constructor of all subclasses
17362      * @method initFrame
17363      */
17364     initFrame: function() {
17365         this.createFrame();
17366     },
17367
17368     applyConfig: function() {
17369         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17370
17371         this.resizeFrame = (this.config.resizeFrame !== false);
17372         this.centerFrame = (this.config.centerFrame);
17373         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17374     },
17375
17376     /**
17377      * Resizes the drag frame to the dimensions of the clicked object, positions
17378      * it over the object, and finally displays it
17379      * @method showFrame
17380      * @param {int} iPageX X click position
17381      * @param {int} iPageY Y click position
17382      * @private
17383      */
17384     showFrame: function(iPageX, iPageY) {
17385         var el = this.getEl();
17386         var dragEl = this.getDragEl();
17387         var s = dragEl.style;
17388
17389         this._resizeProxy();
17390
17391         if (this.centerFrame) {
17392             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17393                            Math.round(parseInt(s.height, 10)/2) );
17394         }
17395
17396         this.setDragElPos(iPageX, iPageY);
17397
17398         Roo.fly(dragEl).show();
17399     },
17400
17401     /**
17402      * The proxy is automatically resized to the dimensions of the linked
17403      * element when a drag is initiated, unless resizeFrame is set to false
17404      * @method _resizeProxy
17405      * @private
17406      */
17407     _resizeProxy: function() {
17408         if (this.resizeFrame) {
17409             var el = this.getEl();
17410             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17411         }
17412     },
17413
17414     // overrides Roo.dd.DragDrop
17415     b4MouseDown: function(e) {
17416         var x = e.getPageX();
17417         var y = e.getPageY();
17418         this.autoOffset(x, y);
17419         this.setDragElPos(x, y);
17420     },
17421
17422     // overrides Roo.dd.DragDrop
17423     b4StartDrag: function(x, y) {
17424         // show the drag frame
17425         this.showFrame(x, y);
17426     },
17427
17428     // overrides Roo.dd.DragDrop
17429     b4EndDrag: function(e) {
17430         Roo.fly(this.getDragEl()).hide();
17431     },
17432
17433     // overrides Roo.dd.DragDrop
17434     // By default we try to move the element to the last location of the frame.
17435     // This is so that the default behavior mirrors that of Roo.dd.DD.
17436     endDrag: function(e) {
17437
17438         var lel = this.getEl();
17439         var del = this.getDragEl();
17440
17441         // Show the drag frame briefly so we can get its position
17442         del.style.visibility = "";
17443
17444         this.beforeMove();
17445         // Hide the linked element before the move to get around a Safari
17446         // rendering bug.
17447         lel.style.visibility = "hidden";
17448         Roo.dd.DDM.moveToEl(lel, del);
17449         del.style.visibility = "hidden";
17450         lel.style.visibility = "";
17451
17452         this.afterDrag();
17453     },
17454
17455     beforeMove : function(){
17456
17457     },
17458
17459     afterDrag : function(){
17460
17461     },
17462
17463     toString: function() {
17464         return ("DDProxy " + this.id);
17465     }
17466
17467 });
17468 /*
17469  * Based on:
17470  * Ext JS Library 1.1.1
17471  * Copyright(c) 2006-2007, Ext JS, LLC.
17472  *
17473  * Originally Released Under LGPL - original licence link has changed is not relivant.
17474  *
17475  * Fork - LGPL
17476  * <script type="text/javascript">
17477  */
17478
17479  /**
17480  * @class Roo.dd.DDTarget
17481  * A DragDrop implementation that does not move, but can be a drop
17482  * target.  You would get the same result by simply omitting implementation
17483  * for the event callbacks, but this way we reduce the processing cost of the
17484  * event listener and the callbacks.
17485  * @extends Roo.dd.DragDrop
17486  * @constructor
17487  * @param {String} id the id of the element that is a drop target
17488  * @param {String} sGroup the group of related DragDrop objects
17489  * @param {object} config an object containing configurable attributes
17490  *                 Valid properties for DDTarget in addition to those in
17491  *                 DragDrop:
17492  *                    none
17493  */
17494 Roo.dd.DDTarget = function(id, sGroup, config) {
17495     if (id) {
17496         this.initTarget(id, sGroup, config);
17497     }
17498     if (config.listeners || config.events) { 
17499        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17500             listeners : config.listeners || {}, 
17501             events : config.events || {} 
17502         });    
17503     }
17504 };
17505
17506 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17507 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17508     toString: function() {
17509         return ("DDTarget " + this.id);
17510     }
17511 });
17512 /*
17513  * Based on:
17514  * Ext JS Library 1.1.1
17515  * Copyright(c) 2006-2007, Ext JS, LLC.
17516  *
17517  * Originally Released Under LGPL - original licence link has changed is not relivant.
17518  *
17519  * Fork - LGPL
17520  * <script type="text/javascript">
17521  */
17522  
17523
17524 /**
17525  * @class Roo.dd.ScrollManager
17526  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17527  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17528  * @singleton
17529  */
17530 Roo.dd.ScrollManager = function(){
17531     var ddm = Roo.dd.DragDropMgr;
17532     var els = {};
17533     var dragEl = null;
17534     var proc = {};
17535     
17536     var onStop = function(e){
17537         dragEl = null;
17538         clearProc();
17539     };
17540     
17541     var triggerRefresh = function(){
17542         if(ddm.dragCurrent){
17543              ddm.refreshCache(ddm.dragCurrent.groups);
17544         }
17545     };
17546     
17547     var doScroll = function(){
17548         if(ddm.dragCurrent){
17549             var dds = Roo.dd.ScrollManager;
17550             if(!dds.animate){
17551                 if(proc.el.scroll(proc.dir, dds.increment)){
17552                     triggerRefresh();
17553                 }
17554             }else{
17555                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17556             }
17557         }
17558     };
17559     
17560     var clearProc = function(){
17561         if(proc.id){
17562             clearInterval(proc.id);
17563         }
17564         proc.id = 0;
17565         proc.el = null;
17566         proc.dir = "";
17567     };
17568     
17569     var startProc = function(el, dir){
17570         clearProc();
17571         proc.el = el;
17572         proc.dir = dir;
17573         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17574     };
17575     
17576     var onFire = function(e, isDrop){
17577         if(isDrop || !ddm.dragCurrent){ return; }
17578         var dds = Roo.dd.ScrollManager;
17579         if(!dragEl || dragEl != ddm.dragCurrent){
17580             dragEl = ddm.dragCurrent;
17581             // refresh regions on drag start
17582             dds.refreshCache();
17583         }
17584         
17585         var xy = Roo.lib.Event.getXY(e);
17586         var pt = new Roo.lib.Point(xy[0], xy[1]);
17587         for(var id in els){
17588             var el = els[id], r = el._region;
17589             if(r && r.contains(pt) && el.isScrollable()){
17590                 if(r.bottom - pt.y <= dds.thresh){
17591                     if(proc.el != el){
17592                         startProc(el, "down");
17593                     }
17594                     return;
17595                 }else if(r.right - pt.x <= dds.thresh){
17596                     if(proc.el != el){
17597                         startProc(el, "left");
17598                     }
17599                     return;
17600                 }else if(pt.y - r.top <= dds.thresh){
17601                     if(proc.el != el){
17602                         startProc(el, "up");
17603                     }
17604                     return;
17605                 }else if(pt.x - r.left <= dds.thresh){
17606                     if(proc.el != el){
17607                         startProc(el, "right");
17608                     }
17609                     return;
17610                 }
17611             }
17612         }
17613         clearProc();
17614     };
17615     
17616     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17617     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17618     
17619     return {
17620         /**
17621          * Registers new overflow element(s) to auto scroll
17622          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17623          */
17624         register : function(el){
17625             if(el instanceof Array){
17626                 for(var i = 0, len = el.length; i < len; i++) {
17627                         this.register(el[i]);
17628                 }
17629             }else{
17630                 el = Roo.get(el);
17631                 els[el.id] = el;
17632             }
17633         },
17634         
17635         /**
17636          * Unregisters overflow element(s) so they are no longer scrolled
17637          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17638          */
17639         unregister : function(el){
17640             if(el instanceof Array){
17641                 for(var i = 0, len = el.length; i < len; i++) {
17642                         this.unregister(el[i]);
17643                 }
17644             }else{
17645                 el = Roo.get(el);
17646                 delete els[el.id];
17647             }
17648         },
17649         
17650         /**
17651          * The number of pixels from the edge of a container the pointer needs to be to 
17652          * trigger scrolling (defaults to 25)
17653          * @type Number
17654          */
17655         thresh : 25,
17656         
17657         /**
17658          * The number of pixels to scroll in each scroll increment (defaults to 50)
17659          * @type Number
17660          */
17661         increment : 100,
17662         
17663         /**
17664          * The frequency of scrolls in milliseconds (defaults to 500)
17665          * @type Number
17666          */
17667         frequency : 500,
17668         
17669         /**
17670          * True to animate the scroll (defaults to true)
17671          * @type Boolean
17672          */
17673         animate: true,
17674         
17675         /**
17676          * The animation duration in seconds - 
17677          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17678          * @type Number
17679          */
17680         animDuration: .4,
17681         
17682         /**
17683          * Manually trigger a cache refresh.
17684          */
17685         refreshCache : function(){
17686             for(var id in els){
17687                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17688                     els[id]._region = els[id].getRegion();
17689                 }
17690             }
17691         }
17692     };
17693 }();/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.dd.Registry
17707  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17708  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17709  * @singleton
17710  */
17711 Roo.dd.Registry = function(){
17712     var elements = {}; 
17713     var handles = {}; 
17714     var autoIdSeed = 0;
17715
17716     var getId = function(el, autogen){
17717         if(typeof el == "string"){
17718             return el;
17719         }
17720         var id = el.id;
17721         if(!id && autogen !== false){
17722             id = "roodd-" + (++autoIdSeed);
17723             el.id = id;
17724         }
17725         return id;
17726     };
17727     
17728     return {
17729     /**
17730      * Register a drag drop element
17731      * @param {String|HTMLElement} element The id or DOM node to register
17732      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17733      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17734      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17735      * populated in the data object (if applicable):
17736      * <pre>
17737 Value      Description<br />
17738 ---------  ------------------------------------------<br />
17739 handles    Array of DOM nodes that trigger dragging<br />
17740            for the element being registered<br />
17741 isHandle   True if the element passed in triggers<br />
17742            dragging itself, else false
17743 </pre>
17744      */
17745         register : function(el, data){
17746             data = data || {};
17747             if(typeof el == "string"){
17748                 el = document.getElementById(el);
17749             }
17750             data.ddel = el;
17751             elements[getId(el)] = data;
17752             if(data.isHandle !== false){
17753                 handles[data.ddel.id] = data;
17754             }
17755             if(data.handles){
17756                 var hs = data.handles;
17757                 for(var i = 0, len = hs.length; i < len; i++){
17758                         handles[getId(hs[i])] = data;
17759                 }
17760             }
17761         },
17762
17763     /**
17764      * Unregister a drag drop element
17765      * @param {String|HTMLElement}  element The id or DOM node to unregister
17766      */
17767         unregister : function(el){
17768             var id = getId(el, false);
17769             var data = elements[id];
17770             if(data){
17771                 delete elements[id];
17772                 if(data.handles){
17773                     var hs = data.handles;
17774                     for(var i = 0, len = hs.length; i < len; i++){
17775                         delete handles[getId(hs[i], false)];
17776                     }
17777                 }
17778             }
17779         },
17780
17781     /**
17782      * Returns the handle registered for a DOM Node by id
17783      * @param {String|HTMLElement} id The DOM node or id to look up
17784      * @return {Object} handle The custom handle data
17785      */
17786         getHandle : function(id){
17787             if(typeof id != "string"){ // must be element?
17788                 id = id.id;
17789             }
17790             return handles[id];
17791         },
17792
17793     /**
17794      * Returns the handle that is registered for the DOM node that is the target of the event
17795      * @param {Event} e The event
17796      * @return {Object} handle The custom handle data
17797      */
17798         getHandleFromEvent : function(e){
17799             var t = Roo.lib.Event.getTarget(e);
17800             return t ? handles[t.id] : null;
17801         },
17802
17803     /**
17804      * Returns a custom data object that is registered for a DOM node by id
17805      * @param {String|HTMLElement} id The DOM node or id to look up
17806      * @return {Object} data The custom data
17807      */
17808         getTarget : function(id){
17809             if(typeof id != "string"){ // must be element?
17810                 id = id.id;
17811             }
17812             return elements[id];
17813         },
17814
17815     /**
17816      * Returns a custom data object that is registered for the DOM node that is the target of the event
17817      * @param {Event} e The event
17818      * @return {Object} data The custom data
17819      */
17820         getTargetFromEvent : function(e){
17821             var t = Roo.lib.Event.getTarget(e);
17822             return t ? elements[t.id] || handles[t.id] : null;
17823         }
17824     };
17825 }();/*
17826  * Based on:
17827  * Ext JS Library 1.1.1
17828  * Copyright(c) 2006-2007, Ext JS, LLC.
17829  *
17830  * Originally Released Under LGPL - original licence link has changed is not relivant.
17831  *
17832  * Fork - LGPL
17833  * <script type="text/javascript">
17834  */
17835  
17836
17837 /**
17838  * @class Roo.dd.StatusProxy
17839  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17840  * default drag proxy used by all Roo.dd components.
17841  * @constructor
17842  * @param {Object} config
17843  */
17844 Roo.dd.StatusProxy = function(config){
17845     Roo.apply(this, config);
17846     this.id = this.id || Roo.id();
17847     this.el = new Roo.Layer({
17848         dh: {
17849             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17850                 {tag: "div", cls: "x-dd-drop-icon"},
17851                 {tag: "div", cls: "x-dd-drag-ghost"}
17852             ]
17853         }, 
17854         shadow: !config || config.shadow !== false
17855     });
17856     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17857     this.dropStatus = this.dropNotAllowed;
17858 };
17859
17860 Roo.dd.StatusProxy.prototype = {
17861     /**
17862      * @cfg {String} dropAllowed
17863      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17864      */
17865     dropAllowed : "x-dd-drop-ok",
17866     /**
17867      * @cfg {String} dropNotAllowed
17868      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17869      */
17870     dropNotAllowed : "x-dd-drop-nodrop",
17871
17872     /**
17873      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17874      * over the current target element.
17875      * @param {String} cssClass The css class for the new drop status indicator image
17876      */
17877     setStatus : function(cssClass){
17878         cssClass = cssClass || this.dropNotAllowed;
17879         if(this.dropStatus != cssClass){
17880             this.el.replaceClass(this.dropStatus, cssClass);
17881             this.dropStatus = cssClass;
17882         }
17883     },
17884
17885     /**
17886      * Resets the status indicator to the default dropNotAllowed value
17887      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17888      */
17889     reset : function(clearGhost){
17890         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17891         this.dropStatus = this.dropNotAllowed;
17892         if(clearGhost){
17893             this.ghost.update("");
17894         }
17895     },
17896
17897     /**
17898      * Updates the contents of the ghost element
17899      * @param {String} html The html that will replace the current innerHTML of the ghost element
17900      */
17901     update : function(html){
17902         if(typeof html == "string"){
17903             this.ghost.update(html);
17904         }else{
17905             this.ghost.update("");
17906             html.style.margin = "0";
17907             this.ghost.dom.appendChild(html);
17908         }
17909         // ensure float = none set?? cant remember why though.
17910         var el = this.ghost.dom.firstChild;
17911                 if(el){
17912                         Roo.fly(el).setStyle('float', 'none');
17913                 }
17914     },
17915     
17916     /**
17917      * Returns the underlying proxy {@link Roo.Layer}
17918      * @return {Roo.Layer} el
17919     */
17920     getEl : function(){
17921         return this.el;
17922     },
17923
17924     /**
17925      * Returns the ghost element
17926      * @return {Roo.Element} el
17927      */
17928     getGhost : function(){
17929         return this.ghost;
17930     },
17931
17932     /**
17933      * Hides the proxy
17934      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17935      */
17936     hide : function(clear){
17937         this.el.hide();
17938         if(clear){
17939             this.reset(true);
17940         }
17941     },
17942
17943     /**
17944      * Stops the repair animation if it's currently running
17945      */
17946     stop : function(){
17947         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17948             this.anim.stop();
17949         }
17950     },
17951
17952     /**
17953      * Displays this proxy
17954      */
17955     show : function(){
17956         this.el.show();
17957     },
17958
17959     /**
17960      * Force the Layer to sync its shadow and shim positions to the element
17961      */
17962     sync : function(){
17963         this.el.sync();
17964     },
17965
17966     /**
17967      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17968      * invalid drop operation by the item being dragged.
17969      * @param {Array} xy The XY position of the element ([x, y])
17970      * @param {Function} callback The function to call after the repair is complete
17971      * @param {Object} scope The scope in which to execute the callback
17972      */
17973     repair : function(xy, callback, scope){
17974         this.callback = callback;
17975         this.scope = scope;
17976         if(xy && this.animRepair !== false){
17977             this.el.addClass("x-dd-drag-repair");
17978             this.el.hideUnders(true);
17979             this.anim = this.el.shift({
17980                 duration: this.repairDuration || .5,
17981                 easing: 'easeOut',
17982                 xy: xy,
17983                 stopFx: true,
17984                 callback: this.afterRepair,
17985                 scope: this
17986             });
17987         }else{
17988             this.afterRepair();
17989         }
17990     },
17991
17992     // private
17993     afterRepair : function(){
17994         this.hide(true);
17995         if(typeof this.callback == "function"){
17996             this.callback.call(this.scope || this);
17997         }
17998         this.callback = null;
17999         this.scope = null;
18000     }
18001 };/*
18002  * Based on:
18003  * Ext JS Library 1.1.1
18004  * Copyright(c) 2006-2007, Ext JS, LLC.
18005  *
18006  * Originally Released Under LGPL - original licence link has changed is not relivant.
18007  *
18008  * Fork - LGPL
18009  * <script type="text/javascript">
18010  */
18011
18012 /**
18013  * @class Roo.dd.DragSource
18014  * @extends Roo.dd.DDProxy
18015  * A simple class that provides the basic implementation needed to make any element draggable.
18016  * @constructor
18017  * @param {String/HTMLElement/Element} el The container element
18018  * @param {Object} config
18019  */
18020 Roo.dd.DragSource = function(el, config){
18021     this.el = Roo.get(el);
18022     this.dragData = {};
18023     
18024     Roo.apply(this, config);
18025     
18026     if(!this.proxy){
18027         this.proxy = new Roo.dd.StatusProxy();
18028     }
18029
18030     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
18031           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
18032     
18033     this.dragging = false;
18034 };
18035
18036 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
18037     /**
18038      * @cfg {String} dropAllowed
18039      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18040      */
18041     dropAllowed : "x-dd-drop-ok",
18042     /**
18043      * @cfg {String} dropNotAllowed
18044      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18045      */
18046     dropNotAllowed : "x-dd-drop-nodrop",
18047
18048     /**
18049      * Returns the data object associated with this drag source
18050      * @return {Object} data An object containing arbitrary data
18051      */
18052     getDragData : function(e){
18053         return this.dragData;
18054     },
18055
18056     // private
18057     onDragEnter : function(e, id){
18058         var target = Roo.dd.DragDropMgr.getDDById(id);
18059         this.cachedTarget = target;
18060         if(this.beforeDragEnter(target, e, id) !== false){
18061             if(target.isNotifyTarget){
18062                 var status = target.notifyEnter(this, e, this.dragData);
18063                 this.proxy.setStatus(status);
18064             }else{
18065                 this.proxy.setStatus(this.dropAllowed);
18066             }
18067             
18068             if(this.afterDragEnter){
18069                 /**
18070                  * An empty function by default, but provided so that you can perform a custom action
18071                  * when the dragged item enters the drop target by providing an implementation.
18072                  * @param {Roo.dd.DragDrop} target The drop target
18073                  * @param {Event} e The event object
18074                  * @param {String} id The id of the dragged element
18075                  * @method afterDragEnter
18076                  */
18077                 this.afterDragEnter(target, e, id);
18078             }
18079         }
18080     },
18081
18082     /**
18083      * An empty function by default, but provided so that you can perform a custom action
18084      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
18085      * @param {Roo.dd.DragDrop} target The drop target
18086      * @param {Event} e The event object
18087      * @param {String} id The id of the dragged element
18088      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18089      */
18090     beforeDragEnter : function(target, e, id){
18091         return true;
18092     },
18093
18094     // private
18095     alignElWithMouse: function() {
18096         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
18097         this.proxy.sync();
18098     },
18099
18100     // private
18101     onDragOver : function(e, id){
18102         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18103         if(this.beforeDragOver(target, e, id) !== false){
18104             if(target.isNotifyTarget){
18105                 var status = target.notifyOver(this, e, this.dragData);
18106                 this.proxy.setStatus(status);
18107             }
18108
18109             if(this.afterDragOver){
18110                 /**
18111                  * An empty function by default, but provided so that you can perform a custom action
18112                  * while the dragged item is over the drop target by providing an implementation.
18113                  * @param {Roo.dd.DragDrop} target The drop target
18114                  * @param {Event} e The event object
18115                  * @param {String} id The id of the dragged element
18116                  * @method afterDragOver
18117                  */
18118                 this.afterDragOver(target, e, id);
18119             }
18120         }
18121     },
18122
18123     /**
18124      * An empty function by default, but provided so that you can perform a custom action
18125      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18126      * @param {Roo.dd.DragDrop} target The drop target
18127      * @param {Event} e The event object
18128      * @param {String} id The id of the dragged element
18129      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18130      */
18131     beforeDragOver : function(target, e, id){
18132         return true;
18133     },
18134
18135     // private
18136     onDragOut : function(e, id){
18137         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18138         if(this.beforeDragOut(target, e, id) !== false){
18139             if(target.isNotifyTarget){
18140                 target.notifyOut(this, e, this.dragData);
18141             }
18142             this.proxy.reset();
18143             if(this.afterDragOut){
18144                 /**
18145                  * An empty function by default, but provided so that you can perform a custom action
18146                  * after the dragged item is dragged out of the target without dropping.
18147                  * @param {Roo.dd.DragDrop} target The drop target
18148                  * @param {Event} e The event object
18149                  * @param {String} id The id of the dragged element
18150                  * @method afterDragOut
18151                  */
18152                 this.afterDragOut(target, e, id);
18153             }
18154         }
18155         this.cachedTarget = null;
18156     },
18157
18158     /**
18159      * An empty function by default, but provided so that you can perform a custom action before the dragged
18160      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18161      * @param {Roo.dd.DragDrop} target The drop target
18162      * @param {Event} e The event object
18163      * @param {String} id The id of the dragged element
18164      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18165      */
18166     beforeDragOut : function(target, e, id){
18167         return true;
18168     },
18169     
18170     // private
18171     onDragDrop : function(e, id){
18172         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18173         if(this.beforeDragDrop(target, e, id) !== false){
18174             if(target.isNotifyTarget){
18175                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18176                     this.onValidDrop(target, e, id);
18177                 }else{
18178                     this.onInvalidDrop(target, e, id);
18179                 }
18180             }else{
18181                 this.onValidDrop(target, e, id);
18182             }
18183             
18184             if(this.afterDragDrop){
18185                 /**
18186                  * An empty function by default, but provided so that you can perform a custom action
18187                  * after a valid drag drop has occurred by providing an implementation.
18188                  * @param {Roo.dd.DragDrop} target The drop target
18189                  * @param {Event} e The event object
18190                  * @param {String} id The id of the dropped element
18191                  * @method afterDragDrop
18192                  */
18193                 this.afterDragDrop(target, e, id);
18194             }
18195         }
18196         delete this.cachedTarget;
18197     },
18198
18199     /**
18200      * An empty function by default, but provided so that you can perform a custom action before the dragged
18201      * item is dropped onto the target and optionally cancel the onDragDrop.
18202      * @param {Roo.dd.DragDrop} target The drop target
18203      * @param {Event} e The event object
18204      * @param {String} id The id of the dragged element
18205      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18206      */
18207     beforeDragDrop : function(target, e, id){
18208         return true;
18209     },
18210
18211     // private
18212     onValidDrop : function(target, e, id){
18213         this.hideProxy();
18214         if(this.afterValidDrop){
18215             /**
18216              * An empty function by default, but provided so that you can perform a custom action
18217              * after a valid drop has occurred by providing an implementation.
18218              * @param {Object} target The target DD 
18219              * @param {Event} e The event object
18220              * @param {String} id The id of the dropped element
18221              * @method afterInvalidDrop
18222              */
18223             this.afterValidDrop(target, e, id);
18224         }
18225     },
18226
18227     // private
18228     getRepairXY : function(e, data){
18229         return this.el.getXY();  
18230     },
18231
18232     // private
18233     onInvalidDrop : function(target, e, id){
18234         this.beforeInvalidDrop(target, e, id);
18235         if(this.cachedTarget){
18236             if(this.cachedTarget.isNotifyTarget){
18237                 this.cachedTarget.notifyOut(this, e, this.dragData);
18238             }
18239             this.cacheTarget = null;
18240         }
18241         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18242
18243         if(this.afterInvalidDrop){
18244             /**
18245              * An empty function by default, but provided so that you can perform a custom action
18246              * after an invalid drop has occurred by providing an implementation.
18247              * @param {Event} e The event object
18248              * @param {String} id The id of the dropped element
18249              * @method afterInvalidDrop
18250              */
18251             this.afterInvalidDrop(e, id);
18252         }
18253     },
18254
18255     // private
18256     afterRepair : function(){
18257         if(Roo.enableFx){
18258             this.el.highlight(this.hlColor || "c3daf9");
18259         }
18260         this.dragging = false;
18261     },
18262
18263     /**
18264      * An empty function by default, but provided so that you can perform a custom action after an invalid
18265      * drop has occurred.
18266      * @param {Roo.dd.DragDrop} target The drop target
18267      * @param {Event} e The event object
18268      * @param {String} id The id of the dragged element
18269      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18270      */
18271     beforeInvalidDrop : function(target, e, id){
18272         return true;
18273     },
18274
18275     // private
18276     handleMouseDown : function(e){
18277         if(this.dragging) {
18278             return;
18279         }
18280         var data = this.getDragData(e);
18281         if(data && this.onBeforeDrag(data, e) !== false){
18282             this.dragData = data;
18283             this.proxy.stop();
18284             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18285         } 
18286     },
18287
18288     /**
18289      * An empty function by default, but provided so that you can perform a custom action before the initial
18290      * drag event begins and optionally cancel it.
18291      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18292      * @param {Event} e The event object
18293      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18294      */
18295     onBeforeDrag : function(data, e){
18296         return true;
18297     },
18298
18299     /**
18300      * An empty function by default, but provided so that you can perform a custom action once the initial
18301      * drag event has begun.  The drag cannot be canceled from this function.
18302      * @param {Number} x The x position of the click on the dragged object
18303      * @param {Number} y The y position of the click on the dragged object
18304      */
18305     onStartDrag : Roo.emptyFn,
18306
18307     // private - YUI override
18308     startDrag : function(x, y){
18309         this.proxy.reset();
18310         this.dragging = true;
18311         this.proxy.update("");
18312         this.onInitDrag(x, y);
18313         this.proxy.show();
18314     },
18315
18316     // private
18317     onInitDrag : function(x, y){
18318         var clone = this.el.dom.cloneNode(true);
18319         clone.id = Roo.id(); // prevent duplicate ids
18320         this.proxy.update(clone);
18321         this.onStartDrag(x, y);
18322         return true;
18323     },
18324
18325     /**
18326      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18327      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18328      */
18329     getProxy : function(){
18330         return this.proxy;  
18331     },
18332
18333     /**
18334      * Hides the drag source's {@link Roo.dd.StatusProxy}
18335      */
18336     hideProxy : function(){
18337         this.proxy.hide();  
18338         this.proxy.reset(true);
18339         this.dragging = false;
18340     },
18341
18342     // private
18343     triggerCacheRefresh : function(){
18344         Roo.dd.DDM.refreshCache(this.groups);
18345     },
18346
18347     // private - override to prevent hiding
18348     b4EndDrag: function(e) {
18349     },
18350
18351     // private - override to prevent moving
18352     endDrag : function(e){
18353         this.onEndDrag(this.dragData, e);
18354     },
18355
18356     // private
18357     onEndDrag : function(data, e){
18358     },
18359     
18360     // private - pin to cursor
18361     autoOffset : function(x, y) {
18362         this.setDelta(-12, -20);
18363     }    
18364 });/*
18365  * Based on:
18366  * Ext JS Library 1.1.1
18367  * Copyright(c) 2006-2007, Ext JS, LLC.
18368  *
18369  * Originally Released Under LGPL - original licence link has changed is not relivant.
18370  *
18371  * Fork - LGPL
18372  * <script type="text/javascript">
18373  */
18374
18375
18376 /**
18377  * @class Roo.dd.DropTarget
18378  * @extends Roo.dd.DDTarget
18379  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18380  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18381  * @constructor
18382  * @param {String/HTMLElement/Element} el The container element
18383  * @param {Object} config
18384  */
18385 Roo.dd.DropTarget = function(el, config){
18386     this.el = Roo.get(el);
18387     
18388     var listeners = false; ;
18389     if (config && config.listeners) {
18390         listeners= config.listeners;
18391         delete config.listeners;
18392     }
18393     Roo.apply(this, config);
18394     
18395     if(this.containerScroll){
18396         Roo.dd.ScrollManager.register(this.el);
18397     }
18398     this.addEvents( {
18399          /**
18400          * @scope Roo.dd.DropTarget
18401          */
18402          
18403          /**
18404          * @event enter
18405          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18406          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18407          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18408          * 
18409          * IMPORTANT : it should set this.overClass and this.dropAllowed
18410          * 
18411          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18412          * @param {Event} e The event
18413          * @param {Object} data An object containing arbitrary data supplied by the drag source
18414          */
18415         "enter" : true,
18416         
18417          /**
18418          * @event over
18419          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18420          * This method will be called on every mouse movement while the drag source is over the drop target.
18421          * This default implementation simply returns the dropAllowed config value.
18422          * 
18423          * IMPORTANT : it should set this.dropAllowed
18424          * 
18425          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18426          * @param {Event} e The event
18427          * @param {Object} data An object containing arbitrary data supplied by the drag source
18428          
18429          */
18430         "over" : true,
18431         /**
18432          * @event out
18433          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18434          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18435          * overClass (if any) from the drop element.
18436          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18437          * @param {Event} e The event
18438          * @param {Object} data An object containing arbitrary data supplied by the drag source
18439          */
18440          "out" : true,
18441          
18442         /**
18443          * @event drop
18444          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18445          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18446          * implementation that does something to process the drop event and returns true so that the drag source's
18447          * repair action does not run.
18448          * 
18449          * IMPORTANT : it should set this.success
18450          * 
18451          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18452          * @param {Event} e The event
18453          * @param {Object} data An object containing arbitrary data supplied by the drag source
18454         */
18455          "drop" : true
18456     });
18457             
18458      
18459     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18460         this.el.dom, 
18461         this.ddGroup || this.group,
18462         {
18463             isTarget: true,
18464             listeners : listeners || {} 
18465            
18466         
18467         }
18468     );
18469
18470 };
18471
18472 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18473     /**
18474      * @cfg {String} overClass
18475      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18476      */
18477      /**
18478      * @cfg {String} ddGroup
18479      * The drag drop group to handle drop events for
18480      */
18481      
18482     /**
18483      * @cfg {String} dropAllowed
18484      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18485      */
18486     dropAllowed : "x-dd-drop-ok",
18487     /**
18488      * @cfg {String} dropNotAllowed
18489      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18490      */
18491     dropNotAllowed : "x-dd-drop-nodrop",
18492     /**
18493      * @cfg {boolean} success
18494      * set this after drop listener.. 
18495      */
18496     success : false,
18497     /**
18498      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
18499      * if the drop point is valid for over/enter..
18500      */
18501     valid : false,
18502     // private
18503     isTarget : true,
18504
18505     // private
18506     isNotifyTarget : true,
18507     
18508     /**
18509      * @hide
18510      */
18511     notifyEnter : function(dd, e, data)
18512     {
18513         this.valid = true;
18514         this.fireEvent('enter', dd, e, data);
18515         if(this.overClass){
18516             this.el.addClass(this.overClass);
18517         }
18518         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18519             this.valid ? this.dropAllowed : this.dropNotAllowed
18520         );
18521     },
18522
18523     /**
18524      * @hide
18525      */
18526     notifyOver : function(dd, e, data)
18527     {
18528         this.valid = true;
18529         this.fireEvent('over', dd, e, data);
18530         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18531             this.valid ? this.dropAllowed : this.dropNotAllowed
18532         );
18533     },
18534
18535     /**
18536      * @hide
18537      */
18538     notifyOut : function(dd, e, data)
18539     {
18540         this.fireEvent('out', dd, e, data);
18541         if(this.overClass){
18542             this.el.removeClass(this.overClass);
18543         }
18544     },
18545
18546     /**
18547      * @hide
18548      */
18549     notifyDrop : function(dd, e, data)
18550     {
18551         this.success = false;
18552         this.fireEvent('drop', dd, e, data);
18553         return this.success;
18554     }
18555 });/*
18556  * Based on:
18557  * Ext JS Library 1.1.1
18558  * Copyright(c) 2006-2007, Ext JS, LLC.
18559  *
18560  * Originally Released Under LGPL - original licence link has changed is not relivant.
18561  *
18562  * Fork - LGPL
18563  * <script type="text/javascript">
18564  */
18565
18566
18567 /**
18568  * @class Roo.dd.DragZone
18569  * @extends Roo.dd.DragSource
18570  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18571  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18572  * @constructor
18573  * @param {String/HTMLElement/Element} el The container element
18574  * @param {Object} config
18575  */
18576 Roo.dd.DragZone = function(el, config){
18577     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18578     if(this.containerScroll){
18579         Roo.dd.ScrollManager.register(this.el);
18580     }
18581 };
18582
18583 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18584     /**
18585      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18586      * for auto scrolling during drag operations.
18587      */
18588     /**
18589      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18590      * method after a failed drop (defaults to "c3daf9" - light blue)
18591      */
18592
18593     /**
18594      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18595      * for a valid target to drag based on the mouse down. Override this method
18596      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18597      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18598      * @param {EventObject} e The mouse down event
18599      * @return {Object} The dragData
18600      */
18601     getDragData : function(e){
18602         return Roo.dd.Registry.getHandleFromEvent(e);
18603     },
18604     
18605     /**
18606      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18607      * this.dragData.ddel
18608      * @param {Number} x The x position of the click on the dragged object
18609      * @param {Number} y The y position of the click on the dragged object
18610      * @return {Boolean} true to continue the drag, false to cancel
18611      */
18612     onInitDrag : function(x, y){
18613         this.proxy.update(this.dragData.ddel.cloneNode(true));
18614         this.onStartDrag(x, y);
18615         return true;
18616     },
18617     
18618     /**
18619      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18620      */
18621     afterRepair : function(){
18622         if(Roo.enableFx){
18623             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18624         }
18625         this.dragging = false;
18626     },
18627
18628     /**
18629      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18630      * the XY of this.dragData.ddel
18631      * @param {EventObject} e The mouse up event
18632      * @return {Array} The xy location (e.g. [100, 200])
18633      */
18634     getRepairXY : function(e){
18635         return Roo.Element.fly(this.dragData.ddel).getXY();  
18636     }
18637 });/*
18638  * Based on:
18639  * Ext JS Library 1.1.1
18640  * Copyright(c) 2006-2007, Ext JS, LLC.
18641  *
18642  * Originally Released Under LGPL - original licence link has changed is not relivant.
18643  *
18644  * Fork - LGPL
18645  * <script type="text/javascript">
18646  */
18647 /**
18648  * @class Roo.dd.DropZone
18649  * @extends Roo.dd.DropTarget
18650  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18651  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18652  * @constructor
18653  * @param {String/HTMLElement/Element} el The container element
18654  * @param {Object} config
18655  */
18656 Roo.dd.DropZone = function(el, config){
18657     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18658 };
18659
18660 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18661     /**
18662      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18663      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18664      * provide your own custom lookup.
18665      * @param {Event} e The event
18666      * @return {Object} data The custom data
18667      */
18668     getTargetFromEvent : function(e){
18669         return Roo.dd.Registry.getTargetFromEvent(e);
18670     },
18671
18672     /**
18673      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18674      * that it has registered.  This method has no default implementation and should be overridden to provide
18675      * node-specific processing if necessary.
18676      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18677      * {@link #getTargetFromEvent} for this node)
18678      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18679      * @param {Event} e The event
18680      * @param {Object} data An object containing arbitrary data supplied by the drag source
18681      */
18682     onNodeEnter : function(n, dd, e, data){
18683         
18684     },
18685
18686     /**
18687      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18688      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18689      * overridden to provide the proper feedback.
18690      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18691      * {@link #getTargetFromEvent} for this node)
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18696      * underlying {@link Roo.dd.StatusProxy} can be updated
18697      */
18698     onNodeOver : function(n, dd, e, data){
18699         return this.dropAllowed;
18700     },
18701
18702     /**
18703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18704      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18705      * node-specific processing if necessary.
18706      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18707      * {@link #getTargetFromEvent} for this node)
18708      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18709      * @param {Event} e The event
18710      * @param {Object} data An object containing arbitrary data supplied by the drag source
18711      */
18712     onNodeOut : function(n, dd, e, data){
18713         
18714     },
18715
18716     /**
18717      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18718      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18719      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18720      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18721      * {@link #getTargetFromEvent} for this node)
18722      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18723      * @param {Event} e The event
18724      * @param {Object} data An object containing arbitrary data supplied by the drag source
18725      * @return {Boolean} True if the drop was valid, else false
18726      */
18727     onNodeDrop : function(n, dd, e, data){
18728         return false;
18729     },
18730
18731     /**
18732      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18733      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18734      * it should be overridden to provide the proper feedback if necessary.
18735      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18736      * @param {Event} e The event
18737      * @param {Object} data An object containing arbitrary data supplied by the drag source
18738      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18739      * underlying {@link Roo.dd.StatusProxy} can be updated
18740      */
18741     onContainerOver : function(dd, e, data){
18742         return this.dropNotAllowed;
18743     },
18744
18745     /**
18746      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18747      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18748      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18749      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18750      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18751      * @param {Event} e The event
18752      * @param {Object} data An object containing arbitrary data supplied by the drag source
18753      * @return {Boolean} True if the drop was valid, else false
18754      */
18755     onContainerDrop : function(dd, e, data){
18756         return false;
18757     },
18758
18759     /**
18760      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18761      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18762      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18763      * you should override this method and provide a custom implementation.
18764      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18765      * @param {Event} e The event
18766      * @param {Object} data An object containing arbitrary data supplied by the drag source
18767      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18768      * underlying {@link Roo.dd.StatusProxy} can be updated
18769      */
18770     notifyEnter : function(dd, e, data){
18771         return this.dropNotAllowed;
18772     },
18773
18774     /**
18775      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18776      * This method will be called on every mouse movement while the drag source is over the drop zone.
18777      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18778      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18779      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18780      * registered node, it will call {@link #onContainerOver}.
18781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18782      * @param {Event} e The event
18783      * @param {Object} data An object containing arbitrary data supplied by the drag source
18784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18785      * underlying {@link Roo.dd.StatusProxy} can be updated
18786      */
18787     notifyOver : function(dd, e, data){
18788         var n = this.getTargetFromEvent(e);
18789         if(!n){ // not over valid drop target
18790             if(this.lastOverNode){
18791                 this.onNodeOut(this.lastOverNode, dd, e, data);
18792                 this.lastOverNode = null;
18793             }
18794             return this.onContainerOver(dd, e, data);
18795         }
18796         if(this.lastOverNode != n){
18797             if(this.lastOverNode){
18798                 this.onNodeOut(this.lastOverNode, dd, e, data);
18799             }
18800             this.onNodeEnter(n, dd, e, data);
18801             this.lastOverNode = n;
18802         }
18803         return this.onNodeOver(n, dd, e, data);
18804     },
18805
18806     /**
18807      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18808      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18809      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18811      * @param {Event} e The event
18812      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18813      */
18814     notifyOut : function(dd, e, data){
18815         if(this.lastOverNode){
18816             this.onNodeOut(this.lastOverNode, dd, e, data);
18817             this.lastOverNode = null;
18818         }
18819     },
18820
18821     /**
18822      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18823      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18824      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18825      * otherwise it will call {@link #onContainerDrop}.
18826      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18827      * @param {Event} e The event
18828      * @param {Object} data An object containing arbitrary data supplied by the drag source
18829      * @return {Boolean} True if the drop was valid, else false
18830      */
18831     notifyDrop : function(dd, e, data){
18832         if(this.lastOverNode){
18833             this.onNodeOut(this.lastOverNode, dd, e, data);
18834             this.lastOverNode = null;
18835         }
18836         var n = this.getTargetFromEvent(e);
18837         return n ?
18838             this.onNodeDrop(n, dd, e, data) :
18839             this.onContainerDrop(dd, e, data);
18840     },
18841
18842     // private
18843     triggerCacheRefresh : function(){
18844         Roo.dd.DDM.refreshCache(this.groups);
18845     }  
18846 });/*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856
18857
18858 /**
18859  * @class Roo.data.SortTypes
18860  * @singleton
18861  * Defines the default sorting (casting?) comparison functions used when sorting data.
18862  */
18863 Roo.data.SortTypes = {
18864     /**
18865      * Default sort that does nothing
18866      * @param {Mixed} s The value being converted
18867      * @return {Mixed} The comparison value
18868      */
18869     none : function(s){
18870         return s;
18871     },
18872     
18873     /**
18874      * The regular expression used to strip tags
18875      * @type {RegExp}
18876      * @property
18877      */
18878     stripTagsRE : /<\/?[^>]+>/gi,
18879     
18880     /**
18881      * Strips all HTML tags to sort on text only
18882      * @param {Mixed} s The value being converted
18883      * @return {String} The comparison value
18884      */
18885     asText : function(s){
18886         return String(s).replace(this.stripTagsRE, "");
18887     },
18888     
18889     /**
18890      * Strips all HTML tags to sort on text only - Case insensitive
18891      * @param {Mixed} s The value being converted
18892      * @return {String} The comparison value
18893      */
18894     asUCText : function(s){
18895         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18896     },
18897     
18898     /**
18899      * Case insensitive string
18900      * @param {Mixed} s The value being converted
18901      * @return {String} The comparison value
18902      */
18903     asUCString : function(s) {
18904         return String(s).toUpperCase();
18905     },
18906     
18907     /**
18908      * Date sorting
18909      * @param {Mixed} s The value being converted
18910      * @return {Number} The comparison value
18911      */
18912     asDate : function(s) {
18913         if(!s){
18914             return 0;
18915         }
18916         if(s instanceof Date){
18917             return s.getTime();
18918         }
18919         return Date.parse(String(s));
18920     },
18921     
18922     /**
18923      * Float sorting
18924      * @param {Mixed} s The value being converted
18925      * @return {Float} The comparison value
18926      */
18927     asFloat : function(s) {
18928         var val = parseFloat(String(s).replace(/,/g, ""));
18929         if(isNaN(val)) val = 0;
18930         return val;
18931     },
18932     
18933     /**
18934      * Integer sorting
18935      * @param {Mixed} s The value being converted
18936      * @return {Number} The comparison value
18937      */
18938     asInt : function(s) {
18939         var val = parseInt(String(s).replace(/,/g, ""));
18940         if(isNaN(val)) val = 0;
18941         return val;
18942     }
18943 };/*
18944  * Based on:
18945  * Ext JS Library 1.1.1
18946  * Copyright(c) 2006-2007, Ext JS, LLC.
18947  *
18948  * Originally Released Under LGPL - original licence link has changed is not relivant.
18949  *
18950  * Fork - LGPL
18951  * <script type="text/javascript">
18952  */
18953
18954 /**
18955 * @class Roo.data.Record
18956  * Instances of this class encapsulate both record <em>definition</em> information, and record
18957  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18958  * to access Records cached in an {@link Roo.data.Store} object.<br>
18959  * <p>
18960  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18961  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18962  * objects.<br>
18963  * <p>
18964  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18965  * @constructor
18966  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18967  * {@link #create}. The parameters are the same.
18968  * @param {Array} data An associative Array of data values keyed by the field name.
18969  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18970  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18971  * not specified an integer id is generated.
18972  */
18973 Roo.data.Record = function(data, id){
18974     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18975     this.data = data;
18976 };
18977
18978 /**
18979  * Generate a constructor for a specific record layout.
18980  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18981  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18982  * Each field definition object may contain the following properties: <ul>
18983  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18984  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18985  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18986  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18987  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18988  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18989  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18990  * this may be omitted.</p></li>
18991  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18992  * <ul><li>auto (Default, implies no conversion)</li>
18993  * <li>string</li>
18994  * <li>int</li>
18995  * <li>float</li>
18996  * <li>boolean</li>
18997  * <li>date</li></ul></p></li>
18998  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18999  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
19000  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
19001  * by the Reader into an object that will be stored in the Record. It is passed the
19002  * following parameters:<ul>
19003  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
19004  * </ul></p></li>
19005  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
19006  * </ul>
19007  * <br>usage:<br><pre><code>
19008 var TopicRecord = Roo.data.Record.create(
19009     {name: 'title', mapping: 'topic_title'},
19010     {name: 'author', mapping: 'username'},
19011     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
19012     {name: 'lastPost', mapping: 'post_time', type: 'date'},
19013     {name: 'lastPoster', mapping: 'user2'},
19014     {name: 'excerpt', mapping: 'post_text'}
19015 );
19016
19017 var myNewRecord = new TopicRecord({
19018     title: 'Do my job please',
19019     author: 'noobie',
19020     totalPosts: 1,
19021     lastPost: new Date(),
19022     lastPoster: 'Animal',
19023     excerpt: 'No way dude!'
19024 });
19025 myStore.add(myNewRecord);
19026 </code></pre>
19027  * @method create
19028  * @static
19029  */
19030 Roo.data.Record.create = function(o){
19031     var f = function(){
19032         f.superclass.constructor.apply(this, arguments);
19033     };
19034     Roo.extend(f, Roo.data.Record);
19035     var p = f.prototype;
19036     p.fields = new Roo.util.MixedCollection(false, function(field){
19037         return field.name;
19038     });
19039     for(var i = 0, len = o.length; i < len; i++){
19040         p.fields.add(new Roo.data.Field(o[i]));
19041     }
19042     f.getField = function(name){
19043         return p.fields.get(name);  
19044     };
19045     return f;
19046 };
19047
19048 Roo.data.Record.AUTO_ID = 1000;
19049 Roo.data.Record.EDIT = 'edit';
19050 Roo.data.Record.REJECT = 'reject';
19051 Roo.data.Record.COMMIT = 'commit';
19052
19053 Roo.data.Record.prototype = {
19054     /**
19055      * Readonly flag - true if this record has been modified.
19056      * @type Boolean
19057      */
19058     dirty : false,
19059     editing : false,
19060     error: null,
19061     modified: null,
19062
19063     // private
19064     join : function(store){
19065         this.store = store;
19066     },
19067
19068     /**
19069      * Set the named field to the specified value.
19070      * @param {String} name The name of the field to set.
19071      * @param {Object} value The value to set the field to.
19072      */
19073     set : function(name, value){
19074         if(this.data[name] == value){
19075             return;
19076         }
19077         this.dirty = true;
19078         if(!this.modified){
19079             this.modified = {};
19080         }
19081         if(typeof this.modified[name] == 'undefined'){
19082             this.modified[name] = this.data[name];
19083         }
19084         this.data[name] = value;
19085         if(!this.editing && this.store){
19086             this.store.afterEdit(this);
19087         }       
19088     },
19089
19090     /**
19091      * Get the value of the named field.
19092      * @param {String} name The name of the field to get the value of.
19093      * @return {Object} The value of the field.
19094      */
19095     get : function(name){
19096         return this.data[name]; 
19097     },
19098
19099     // private
19100     beginEdit : function(){
19101         this.editing = true;
19102         this.modified = {}; 
19103     },
19104
19105     // private
19106     cancelEdit : function(){
19107         this.editing = false;
19108         delete this.modified;
19109     },
19110
19111     // private
19112     endEdit : function(){
19113         this.editing = false;
19114         if(this.dirty && this.store){
19115             this.store.afterEdit(this);
19116         }
19117     },
19118
19119     /**
19120      * Usually called by the {@link Roo.data.Store} which owns the Record.
19121      * Rejects all changes made to the Record since either creation, or the last commit operation.
19122      * Modified fields are reverted to their original values.
19123      * <p>
19124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19125      * of reject operations.
19126      */
19127     reject : function(){
19128         var m = this.modified;
19129         for(var n in m){
19130             if(typeof m[n] != "function"){
19131                 this.data[n] = m[n];
19132             }
19133         }
19134         this.dirty = false;
19135         delete this.modified;
19136         this.editing = false;
19137         if(this.store){
19138             this.store.afterReject(this);
19139         }
19140     },
19141
19142     /**
19143      * Usually called by the {@link Roo.data.Store} which owns the Record.
19144      * Commits all changes made to the Record since either creation, or the last commit operation.
19145      * <p>
19146      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19147      * of commit operations.
19148      */
19149     commit : function(){
19150         this.dirty = false;
19151         delete this.modified;
19152         this.editing = false;
19153         if(this.store){
19154             this.store.afterCommit(this);
19155         }
19156     },
19157
19158     // private
19159     hasError : function(){
19160         return this.error != null;
19161     },
19162
19163     // private
19164     clearError : function(){
19165         this.error = null;
19166     },
19167
19168     /**
19169      * Creates a copy of this record.
19170      * @param {String} id (optional) A new record id if you don't want to use this record's id
19171      * @return {Record}
19172      */
19173     copy : function(newId) {
19174         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19175     }
19176 };/*
19177  * Based on:
19178  * Ext JS Library 1.1.1
19179  * Copyright(c) 2006-2007, Ext JS, LLC.
19180  *
19181  * Originally Released Under LGPL - original licence link has changed is not relivant.
19182  *
19183  * Fork - LGPL
19184  * <script type="text/javascript">
19185  */
19186
19187
19188
19189 /**
19190  * @class Roo.data.Store
19191  * @extends Roo.util.Observable
19192  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19193  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19194  * <p>
19195  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
19196  * has no knowledge of the format of the data returned by the Proxy.<br>
19197  * <p>
19198  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19199  * instances from the data object. These records are cached and made available through accessor functions.
19200  * @constructor
19201  * Creates a new Store.
19202  * @param {Object} config A config object containing the objects needed for the Store to access data,
19203  * and read the data into Records.
19204  */
19205 Roo.data.Store = function(config){
19206     this.data = new Roo.util.MixedCollection(false);
19207     this.data.getKey = function(o){
19208         return o.id;
19209     };
19210     this.baseParams = {};
19211     // private
19212     this.paramNames = {
19213         "start" : "start",
19214         "limit" : "limit",
19215         "sort" : "sort",
19216         "dir" : "dir",
19217         "multisort" : "_multisort"
19218     };
19219
19220     if(config && config.data){
19221         this.inlineData = config.data;
19222         delete config.data;
19223     }
19224
19225     Roo.apply(this, config);
19226     
19227     if(this.reader){ // reader passed
19228         this.reader = Roo.factory(this.reader, Roo.data);
19229         this.reader.xmodule = this.xmodule || false;
19230         if(!this.recordType){
19231             this.recordType = this.reader.recordType;
19232         }
19233         if(this.reader.onMetaChange){
19234             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19235         }
19236     }
19237
19238     if(this.recordType){
19239         this.fields = this.recordType.prototype.fields;
19240     }
19241     this.modified = [];
19242
19243     this.addEvents({
19244         /**
19245          * @event datachanged
19246          * Fires when the data cache has changed, and a widget which is using this Store
19247          * as a Record cache should refresh its view.
19248          * @param {Store} this
19249          */
19250         datachanged : true,
19251         /**
19252          * @event metachange
19253          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19254          * @param {Store} this
19255          * @param {Object} meta The JSON metadata
19256          */
19257         metachange : true,
19258         /**
19259          * @event add
19260          * Fires when Records have been added to the Store
19261          * @param {Store} this
19262          * @param {Roo.data.Record[]} records The array of Records added
19263          * @param {Number} index The index at which the record(s) were added
19264          */
19265         add : true,
19266         /**
19267          * @event remove
19268          * Fires when a Record has been removed from the Store
19269          * @param {Store} this
19270          * @param {Roo.data.Record} record The Record that was removed
19271          * @param {Number} index The index at which the record was removed
19272          */
19273         remove : true,
19274         /**
19275          * @event update
19276          * Fires when a Record has been updated
19277          * @param {Store} this
19278          * @param {Roo.data.Record} record The Record that was updated
19279          * @param {String} operation The update operation being performed.  Value may be one of:
19280          * <pre><code>
19281  Roo.data.Record.EDIT
19282  Roo.data.Record.REJECT
19283  Roo.data.Record.COMMIT
19284          * </code></pre>
19285          */
19286         update : true,
19287         /**
19288          * @event clear
19289          * Fires when the data cache has been cleared.
19290          * @param {Store} this
19291          */
19292         clear : true,
19293         /**
19294          * @event beforeload
19295          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19296          * the load action will be canceled.
19297          * @param {Store} this
19298          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19299          */
19300         beforeload : true,
19301         /**
19302          * @event load
19303          * Fires after a new set of Records has been loaded.
19304          * @param {Store} this
19305          * @param {Roo.data.Record[]} records The Records that were loaded
19306          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19307          */
19308         load : true,
19309         /**
19310          * @event loadexception
19311          * Fires if an exception occurs in the Proxy during loading.
19312          * Called with the signature of the Proxy's "loadexception" event.
19313          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19314          * 
19315          * @param {Proxy} 
19316          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19317          * @param {Object} load options 
19318          * @param {Object} jsonData from your request (normally this contains the Exception)
19319          */
19320         loadexception : true
19321     });
19322     
19323     if(this.proxy){
19324         this.proxy = Roo.factory(this.proxy, Roo.data);
19325         this.proxy.xmodule = this.xmodule || false;
19326         this.relayEvents(this.proxy,  ["loadexception"]);
19327     }
19328     this.sortToggle = {};
19329     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
19330
19331     Roo.data.Store.superclass.constructor.call(this);
19332
19333     if(this.inlineData){
19334         this.loadData(this.inlineData);
19335         delete this.inlineData;
19336     }
19337 };
19338 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19339      /**
19340     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19341     * without a remote query - used by combo/forms at present.
19342     */
19343     
19344     /**
19345     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19346     */
19347     /**
19348     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19349     */
19350     /**
19351     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19352     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19353     */
19354     /**
19355     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19356     * on any HTTP request
19357     */
19358     /**
19359     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19360     */
19361     /**
19362     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
19363     */
19364     multiSort: false,
19365     /**
19366     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19367     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19368     */
19369     remoteSort : false,
19370
19371     /**
19372     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19373      * loaded or when a record is removed. (defaults to false).
19374     */
19375     pruneModifiedRecords : false,
19376
19377     // private
19378     lastOptions : null,
19379
19380     /**
19381      * Add Records to the Store and fires the add event.
19382      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19383      */
19384     add : function(records){
19385         records = [].concat(records);
19386         for(var i = 0, len = records.length; i < len; i++){
19387             records[i].join(this);
19388         }
19389         var index = this.data.length;
19390         this.data.addAll(records);
19391         this.fireEvent("add", this, records, index);
19392     },
19393
19394     /**
19395      * Remove a Record from the Store and fires the remove event.
19396      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19397      */
19398     remove : function(record){
19399         var index = this.data.indexOf(record);
19400         this.data.removeAt(index);
19401         if(this.pruneModifiedRecords){
19402             this.modified.remove(record);
19403         }
19404         this.fireEvent("remove", this, record, index);
19405     },
19406
19407     /**
19408      * Remove all Records from the Store and fires the clear event.
19409      */
19410     removeAll : function(){
19411         this.data.clear();
19412         if(this.pruneModifiedRecords){
19413             this.modified = [];
19414         }
19415         this.fireEvent("clear", this);
19416     },
19417
19418     /**
19419      * Inserts Records to the Store at the given index and fires the add event.
19420      * @param {Number} index The start index at which to insert the passed Records.
19421      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19422      */
19423     insert : function(index, records){
19424         records = [].concat(records);
19425         for(var i = 0, len = records.length; i < len; i++){
19426             this.data.insert(index, records[i]);
19427             records[i].join(this);
19428         }
19429         this.fireEvent("add", this, records, index);
19430     },
19431
19432     /**
19433      * Get the index within the cache of the passed Record.
19434      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19435      * @return {Number} The index of the passed Record. Returns -1 if not found.
19436      */
19437     indexOf : function(record){
19438         return this.data.indexOf(record);
19439     },
19440
19441     /**
19442      * Get the index within the cache of the Record with the passed id.
19443      * @param {String} id The id of the Record to find.
19444      * @return {Number} The index of the Record. Returns -1 if not found.
19445      */
19446     indexOfId : function(id){
19447         return this.data.indexOfKey(id);
19448     },
19449
19450     /**
19451      * Get the Record with the specified id.
19452      * @param {String} id The id of the Record to find.
19453      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19454      */
19455     getById : function(id){
19456         return this.data.key(id);
19457     },
19458
19459     /**
19460      * Get the Record at the specified index.
19461      * @param {Number} index The index of the Record to find.
19462      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19463      */
19464     getAt : function(index){
19465         return this.data.itemAt(index);
19466     },
19467
19468     /**
19469      * Returns a range of Records between specified indices.
19470      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19471      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19472      * @return {Roo.data.Record[]} An array of Records
19473      */
19474     getRange : function(start, end){
19475         return this.data.getRange(start, end);
19476     },
19477
19478     // private
19479     storeOptions : function(o){
19480         o = Roo.apply({}, o);
19481         delete o.callback;
19482         delete o.scope;
19483         this.lastOptions = o;
19484     },
19485
19486     /**
19487      * Loads the Record cache from the configured Proxy using the configured Reader.
19488      * <p>
19489      * If using remote paging, then the first load call must specify the <em>start</em>
19490      * and <em>limit</em> properties in the options.params property to establish the initial
19491      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19492      * <p>
19493      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19494      * and this call will return before the new data has been loaded. Perform any post-processing
19495      * in a callback function, or in a "load" event handler.</strong>
19496      * <p>
19497      * @param {Object} options An object containing properties which control loading options:<ul>
19498      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19499      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19500      * passed the following arguments:<ul>
19501      * <li>r : Roo.data.Record[]</li>
19502      * <li>options: Options object from the load call</li>
19503      * <li>success: Boolean success indicator</li></ul></li>
19504      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19505      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19506      * </ul>
19507      */
19508     load : function(options){
19509         options = options || {};
19510         if(this.fireEvent("beforeload", this, options) !== false){
19511             this.storeOptions(options);
19512             var p = Roo.apply(options.params || {}, this.baseParams);
19513             // if meta was not loaded from remote source.. try requesting it.
19514             if (!this.reader.metaFromRemote) {
19515                 p._requestMeta = 1;
19516             }
19517             if(this.sortInfo && this.remoteSort){
19518                 var pn = this.paramNames;
19519                 p[pn["sort"]] = this.sortInfo.field;
19520                 p[pn["dir"]] = this.sortInfo.direction;
19521             }
19522             if (this.multiSort) {
19523                 var pn = this.paramNames;
19524                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
19525             }
19526             
19527             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19528         }
19529     },
19530
19531     /**
19532      * Reloads the Record cache from the configured Proxy using the configured Reader and
19533      * the options from the last load operation performed.
19534      * @param {Object} options (optional) An object containing properties which may override the options
19535      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19536      * the most recently used options are reused).
19537      */
19538     reload : function(options){
19539         this.load(Roo.applyIf(options||{}, this.lastOptions));
19540     },
19541
19542     // private
19543     // Called as a callback by the Reader during a load operation.
19544     loadRecords : function(o, options, success){
19545         if(!o || success === false){
19546             if(success !== false){
19547                 this.fireEvent("load", this, [], options);
19548             }
19549             if(options.callback){
19550                 options.callback.call(options.scope || this, [], options, false);
19551             }
19552             return;
19553         }
19554         // if data returned failure - throw an exception.
19555         if (o.success === false) {
19556             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19557             return;
19558         }
19559         var r = o.records, t = o.totalRecords || r.length;
19560         if(!options || options.add !== true){
19561             if(this.pruneModifiedRecords){
19562                 this.modified = [];
19563             }
19564             for(var i = 0, len = r.length; i < len; i++){
19565                 r[i].join(this);
19566             }
19567             if(this.snapshot){
19568                 this.data = this.snapshot;
19569                 delete this.snapshot;
19570             }
19571             this.data.clear();
19572             this.data.addAll(r);
19573             this.totalLength = t;
19574             this.applySort();
19575             this.fireEvent("datachanged", this);
19576         }else{
19577             this.totalLength = Math.max(t, this.data.length+r.length);
19578             this.add(r);
19579         }
19580         this.fireEvent("load", this, r, options);
19581         if(options.callback){
19582             options.callback.call(options.scope || this, r, options, true);
19583         }
19584     },
19585
19586     /**
19587      * Loads data from a passed data block. A Reader which understands the format of the data
19588      * must have been configured in the constructor.
19589      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19590      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19591      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19592      */
19593     loadData : function(o, append){
19594         var r = this.reader.readRecords(o);
19595         this.loadRecords(r, {add: append}, true);
19596     },
19597
19598     /**
19599      * Gets the number of cached records.
19600      * <p>
19601      * <em>If using paging, this may not be the total size of the dataset. If the data object
19602      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19603      * the data set size</em>
19604      */
19605     getCount : function(){
19606         return this.data.length || 0;
19607     },
19608
19609     /**
19610      * Gets the total number of records in the dataset as returned by the server.
19611      * <p>
19612      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19613      * the dataset size</em>
19614      */
19615     getTotalCount : function(){
19616         return this.totalLength || 0;
19617     },
19618
19619     /**
19620      * Returns the sort state of the Store as an object with two properties:
19621      * <pre><code>
19622  field {String} The name of the field by which the Records are sorted
19623  direction {String} The sort order, "ASC" or "DESC"
19624      * </code></pre>
19625      */
19626     getSortState : function(){
19627         return this.sortInfo;
19628     },
19629
19630     // private
19631     applySort : function(){
19632         if(this.sortInfo && !this.remoteSort){
19633             var s = this.sortInfo, f = s.field;
19634             var st = this.fields.get(f).sortType;
19635             var fn = function(r1, r2){
19636                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19637                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19638             };
19639             this.data.sort(s.direction, fn);
19640             if(this.snapshot && this.snapshot != this.data){
19641                 this.snapshot.sort(s.direction, fn);
19642             }
19643         }
19644     },
19645
19646     /**
19647      * Sets the default sort column and order to be used by the next load operation.
19648      * @param {String} fieldName The name of the field to sort by.
19649      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19650      */
19651     setDefaultSort : function(field, dir){
19652         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19653     },
19654
19655     /**
19656      * Sort the Records.
19657      * If remote sorting is used, the sort is performed on the server, and the cache is
19658      * reloaded. If local sorting is used, the cache is sorted internally.
19659      * @param {String} fieldName The name of the field to sort by.
19660      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19661      */
19662     sort : function(fieldName, dir){
19663         var f = this.fields.get(fieldName);
19664         if(!dir){
19665             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
19666             
19667             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
19668                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19669             }else{
19670                 dir = f.sortDir;
19671             }
19672         }
19673         this.sortToggle[f.name] = dir;
19674         this.sortInfo = {field: f.name, direction: dir};
19675         if(!this.remoteSort){
19676             this.applySort();
19677             this.fireEvent("datachanged", this);
19678         }else{
19679             this.load(this.lastOptions);
19680         }
19681     },
19682
19683     /**
19684      * Calls the specified function for each of the Records in the cache.
19685      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19686      * Returning <em>false</em> aborts and exits the iteration.
19687      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19688      */
19689     each : function(fn, scope){
19690         this.data.each(fn, scope);
19691     },
19692
19693     /**
19694      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19695      * (e.g., during paging).
19696      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19697      */
19698     getModifiedRecords : function(){
19699         return this.modified;
19700     },
19701
19702     // private
19703     createFilterFn : function(property, value, anyMatch){
19704         if(!value.exec){ // not a regex
19705             value = String(value);
19706             if(value.length == 0){
19707                 return false;
19708             }
19709             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19710         }
19711         return function(r){
19712             return value.test(r.data[property]);
19713         };
19714     },
19715
19716     /**
19717      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19718      * @param {String} property A field on your records
19719      * @param {Number} start The record index to start at (defaults to 0)
19720      * @param {Number} end The last record index to include (defaults to length - 1)
19721      * @return {Number} The sum
19722      */
19723     sum : function(property, start, end){
19724         var rs = this.data.items, v = 0;
19725         start = start || 0;
19726         end = (end || end === 0) ? end : rs.length-1;
19727
19728         for(var i = start; i <= end; i++){
19729             v += (rs[i].data[property] || 0);
19730         }
19731         return v;
19732     },
19733
19734     /**
19735      * Filter the records by a specified property.
19736      * @param {String} field A field on your records
19737      * @param {String/RegExp} value Either a string that the field
19738      * should start with or a RegExp to test against the field
19739      * @param {Boolean} anyMatch True to match any part not just the beginning
19740      */
19741     filter : function(property, value, anyMatch){
19742         var fn = this.createFilterFn(property, value, anyMatch);
19743         return fn ? this.filterBy(fn) : this.clearFilter();
19744     },
19745
19746     /**
19747      * Filter by a function. The specified function will be called with each
19748      * record in this data source. If the function returns true the record is included,
19749      * otherwise it is filtered.
19750      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19751      * @param {Object} scope (optional) The scope of the function (defaults to this)
19752      */
19753     filterBy : function(fn, scope){
19754         this.snapshot = this.snapshot || this.data;
19755         this.data = this.queryBy(fn, scope||this);
19756         this.fireEvent("datachanged", this);
19757     },
19758
19759     /**
19760      * Query the records by a specified property.
19761      * @param {String} field A field on your records
19762      * @param {String/RegExp} value Either a string that the field
19763      * should start with or a RegExp to test against the field
19764      * @param {Boolean} anyMatch True to match any part not just the beginning
19765      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19766      */
19767     query : function(property, value, anyMatch){
19768         var fn = this.createFilterFn(property, value, anyMatch);
19769         return fn ? this.queryBy(fn) : this.data.clone();
19770     },
19771
19772     /**
19773      * Query by a function. The specified function will be called with each
19774      * record in this data source. If the function returns true the record is included
19775      * in the results.
19776      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19777      * @param {Object} scope (optional) The scope of the function (defaults to this)
19778       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19779      **/
19780     queryBy : function(fn, scope){
19781         var data = this.snapshot || this.data;
19782         return data.filterBy(fn, scope||this);
19783     },
19784
19785     /**
19786      * Collects unique values for a particular dataIndex from this store.
19787      * @param {String} dataIndex The property to collect
19788      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19789      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19790      * @return {Array} An array of the unique values
19791      **/
19792     collect : function(dataIndex, allowNull, bypassFilter){
19793         var d = (bypassFilter === true && this.snapshot) ?
19794                 this.snapshot.items : this.data.items;
19795         var v, sv, r = [], l = {};
19796         for(var i = 0, len = d.length; i < len; i++){
19797             v = d[i].data[dataIndex];
19798             sv = String(v);
19799             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19800                 l[sv] = true;
19801                 r[r.length] = v;
19802             }
19803         }
19804         return r;
19805     },
19806
19807     /**
19808      * Revert to a view of the Record cache with no filtering applied.
19809      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19810      */
19811     clearFilter : function(suppressEvent){
19812         if(this.snapshot && this.snapshot != this.data){
19813             this.data = this.snapshot;
19814             delete this.snapshot;
19815             if(suppressEvent !== true){
19816                 this.fireEvent("datachanged", this);
19817             }
19818         }
19819     },
19820
19821     // private
19822     afterEdit : function(record){
19823         if(this.modified.indexOf(record) == -1){
19824             this.modified.push(record);
19825         }
19826         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19827     },
19828
19829     // private
19830     afterReject : function(record){
19831         this.modified.remove(record);
19832         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19833     },
19834
19835     // private
19836     afterCommit : function(record){
19837         this.modified.remove(record);
19838         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19839     },
19840
19841     /**
19842      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19843      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19844      */
19845     commitChanges : function(){
19846         var m = this.modified.slice(0);
19847         this.modified = [];
19848         for(var i = 0, len = m.length; i < len; i++){
19849             m[i].commit();
19850         }
19851     },
19852
19853     /**
19854      * Cancel outstanding changes on all changed records.
19855      */
19856     rejectChanges : function(){
19857         var m = this.modified.slice(0);
19858         this.modified = [];
19859         for(var i = 0, len = m.length; i < len; i++){
19860             m[i].reject();
19861         }
19862     },
19863
19864     onMetaChange : function(meta, rtype, o){
19865         this.recordType = rtype;
19866         this.fields = rtype.prototype.fields;
19867         delete this.snapshot;
19868         this.sortInfo = meta.sortInfo || this.sortInfo;
19869         this.modified = [];
19870         this.fireEvent('metachange', this, this.reader.meta);
19871     }
19872 });/*
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  * @class Roo.data.SimpleStore
19885  * @extends Roo.data.Store
19886  * Small helper class to make creating Stores from Array data easier.
19887  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19888  * @cfg {Array} fields An array of field definition objects, or field name strings.
19889  * @cfg {Array} data The multi-dimensional array of data
19890  * @constructor
19891  * @param {Object} config
19892  */
19893 Roo.data.SimpleStore = function(config){
19894     Roo.data.SimpleStore.superclass.constructor.call(this, {
19895         isLocal : true,
19896         reader: new Roo.data.ArrayReader({
19897                 id: config.id
19898             },
19899             Roo.data.Record.create(config.fields)
19900         ),
19901         proxy : new Roo.data.MemoryProxy(config.data)
19902     });
19903     this.load();
19904 };
19905 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19906  * Based on:
19907  * Ext JS Library 1.1.1
19908  * Copyright(c) 2006-2007, Ext JS, LLC.
19909  *
19910  * Originally Released Under LGPL - original licence link has changed is not relivant.
19911  *
19912  * Fork - LGPL
19913  * <script type="text/javascript">
19914  */
19915
19916 /**
19917 /**
19918  * @extends Roo.data.Store
19919  * @class Roo.data.JsonStore
19920  * Small helper class to make creating Stores for JSON data easier. <br/>
19921 <pre><code>
19922 var store = new Roo.data.JsonStore({
19923     url: 'get-images.php',
19924     root: 'images',
19925     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19926 });
19927 </code></pre>
19928  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19929  * JsonReader and HttpProxy (unless inline data is provided).</b>
19930  * @cfg {Array} fields An array of field definition objects, or field name strings.
19931  * @constructor
19932  * @param {Object} config
19933  */
19934 Roo.data.JsonStore = function(c){
19935     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19936         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19937         reader: new Roo.data.JsonReader(c, c.fields)
19938     }));
19939 };
19940 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19941  * Based on:
19942  * Ext JS Library 1.1.1
19943  * Copyright(c) 2006-2007, Ext JS, LLC.
19944  *
19945  * Originally Released Under LGPL - original licence link has changed is not relivant.
19946  *
19947  * Fork - LGPL
19948  * <script type="text/javascript">
19949  */
19950
19951  
19952 Roo.data.Field = function(config){
19953     if(typeof config == "string"){
19954         config = {name: config};
19955     }
19956     Roo.apply(this, config);
19957     
19958     if(!this.type){
19959         this.type = "auto";
19960     }
19961     
19962     var st = Roo.data.SortTypes;
19963     // named sortTypes are supported, here we look them up
19964     if(typeof this.sortType == "string"){
19965         this.sortType = st[this.sortType];
19966     }
19967     
19968     // set default sortType for strings and dates
19969     if(!this.sortType){
19970         switch(this.type){
19971             case "string":
19972                 this.sortType = st.asUCString;
19973                 break;
19974             case "date":
19975                 this.sortType = st.asDate;
19976                 break;
19977             default:
19978                 this.sortType = st.none;
19979         }
19980     }
19981
19982     // define once
19983     var stripRe = /[\$,%]/g;
19984
19985     // prebuilt conversion function for this field, instead of
19986     // switching every time we're reading a value
19987     if(!this.convert){
19988         var cv, dateFormat = this.dateFormat;
19989         switch(this.type){
19990             case "":
19991             case "auto":
19992             case undefined:
19993                 cv = function(v){ return v; };
19994                 break;
19995             case "string":
19996                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19997                 break;
19998             case "int":
19999                 cv = function(v){
20000                     return v !== undefined && v !== null && v !== '' ?
20001                            parseInt(String(v).replace(stripRe, ""), 10) : '';
20002                     };
20003                 break;
20004             case "float":
20005                 cv = function(v){
20006                     return v !== undefined && v !== null && v !== '' ?
20007                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
20008                     };
20009                 break;
20010             case "bool":
20011             case "boolean":
20012                 cv = function(v){ return v === true || v === "true" || v == 1; };
20013                 break;
20014             case "date":
20015                 cv = function(v){
20016                     if(!v){
20017                         return '';
20018                     }
20019                     if(v instanceof Date){
20020                         return v;
20021                     }
20022                     if(dateFormat){
20023                         if(dateFormat == "timestamp"){
20024                             return new Date(v*1000);
20025                         }
20026                         return Date.parseDate(v, dateFormat);
20027                     }
20028                     var parsed = Date.parse(v);
20029                     return parsed ? new Date(parsed) : null;
20030                 };
20031              break;
20032             
20033         }
20034         this.convert = cv;
20035     }
20036 };
20037
20038 Roo.data.Field.prototype = {
20039     dateFormat: null,
20040     defaultValue: "",
20041     mapping: null,
20042     sortType : null,
20043     sortDir : "ASC"
20044 };/*
20045  * Based on:
20046  * Ext JS Library 1.1.1
20047  * Copyright(c) 2006-2007, Ext JS, LLC.
20048  *
20049  * Originally Released Under LGPL - original licence link has changed is not relivant.
20050  *
20051  * Fork - LGPL
20052  * <script type="text/javascript">
20053  */
20054  
20055 // Base class for reading structured data from a data source.  This class is intended to be
20056 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
20057
20058 /**
20059  * @class Roo.data.DataReader
20060  * Base class for reading structured data from a data source.  This class is intended to be
20061  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
20062  */
20063
20064 Roo.data.DataReader = function(meta, recordType){
20065     
20066     this.meta = meta;
20067     
20068     this.recordType = recordType instanceof Array ? 
20069         Roo.data.Record.create(recordType) : recordType;
20070 };
20071
20072 Roo.data.DataReader.prototype = {
20073      /**
20074      * Create an empty record
20075      * @param {Object} data (optional) - overlay some values
20076      * @return {Roo.data.Record} record created.
20077      */
20078     newRow :  function(d) {
20079         var da =  {};
20080         this.recordType.prototype.fields.each(function(c) {
20081             switch( c.type) {
20082                 case 'int' : da[c.name] = 0; break;
20083                 case 'date' : da[c.name] = new Date(); break;
20084                 case 'float' : da[c.name] = 0.0; break;
20085                 case 'boolean' : da[c.name] = false; break;
20086                 default : da[c.name] = ""; break;
20087             }
20088             
20089         });
20090         return new this.recordType(Roo.apply(da, d));
20091     }
20092     
20093 };/*
20094  * Based on:
20095  * Ext JS Library 1.1.1
20096  * Copyright(c) 2006-2007, Ext JS, LLC.
20097  *
20098  * Originally Released Under LGPL - original licence link has changed is not relivant.
20099  *
20100  * Fork - LGPL
20101  * <script type="text/javascript">
20102  */
20103
20104 /**
20105  * @class Roo.data.DataProxy
20106  * @extends Roo.data.Observable
20107  * This class is an abstract base class for implementations which provide retrieval of
20108  * unformatted data objects.<br>
20109  * <p>
20110  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
20111  * (of the appropriate type which knows how to parse the data object) to provide a block of
20112  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
20113  * <p>
20114  * Custom implementations must implement the load method as described in
20115  * {@link Roo.data.HttpProxy#load}.
20116  */
20117 Roo.data.DataProxy = function(){
20118     this.addEvents({
20119         /**
20120          * @event beforeload
20121          * Fires before a network request is made to retrieve a data object.
20122          * @param {Object} This DataProxy object.
20123          * @param {Object} params The params parameter to the load function.
20124          */
20125         beforeload : true,
20126         /**
20127          * @event load
20128          * Fires before the load method's callback is called.
20129          * @param {Object} This DataProxy object.
20130          * @param {Object} o The data object.
20131          * @param {Object} arg The callback argument object passed to the load function.
20132          */
20133         load : true,
20134         /**
20135          * @event loadexception
20136          * Fires if an Exception occurs during data retrieval.
20137          * @param {Object} This DataProxy object.
20138          * @param {Object} o The data object.
20139          * @param {Object} arg The callback argument object passed to the load function.
20140          * @param {Object} e The Exception.
20141          */
20142         loadexception : true
20143     });
20144     Roo.data.DataProxy.superclass.constructor.call(this);
20145 };
20146
20147 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20148
20149     /**
20150      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20151      */
20152 /*
20153  * Based on:
20154  * Ext JS Library 1.1.1
20155  * Copyright(c) 2006-2007, Ext JS, LLC.
20156  *
20157  * Originally Released Under LGPL - original licence link has changed is not relivant.
20158  *
20159  * Fork - LGPL
20160  * <script type="text/javascript">
20161  */
20162 /**
20163  * @class Roo.data.MemoryProxy
20164  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20165  * to the Reader when its load method is called.
20166  * @constructor
20167  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20168  */
20169 Roo.data.MemoryProxy = function(data){
20170     if (data.data) {
20171         data = data.data;
20172     }
20173     Roo.data.MemoryProxy.superclass.constructor.call(this);
20174     this.data = data;
20175 };
20176
20177 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20178     /**
20179      * Load data from the requested source (in this case an in-memory
20180      * data object passed to the constructor), read the data object into
20181      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20182      * process that block using the passed callback.
20183      * @param {Object} params This parameter is not used by the MemoryProxy class.
20184      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20185      * object into a block of Roo.data.Records.
20186      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20187      * The function must be passed <ul>
20188      * <li>The Record block object</li>
20189      * <li>The "arg" argument from the load function</li>
20190      * <li>A boolean success indicator</li>
20191      * </ul>
20192      * @param {Object} scope The scope in which to call the callback
20193      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20194      */
20195     load : function(params, reader, callback, scope, arg){
20196         params = params || {};
20197         var result;
20198         try {
20199             result = reader.readRecords(this.data);
20200         }catch(e){
20201             this.fireEvent("loadexception", this, arg, null, e);
20202             callback.call(scope, null, arg, false);
20203             return;
20204         }
20205         callback.call(scope, result, arg, true);
20206     },
20207     
20208     // private
20209     update : function(params, records){
20210         
20211     }
20212 });/*
20213  * Based on:
20214  * Ext JS Library 1.1.1
20215  * Copyright(c) 2006-2007, Ext JS, LLC.
20216  *
20217  * Originally Released Under LGPL - original licence link has changed is not relivant.
20218  *
20219  * Fork - LGPL
20220  * <script type="text/javascript">
20221  */
20222 /**
20223  * @class Roo.data.HttpProxy
20224  * @extends Roo.data.DataProxy
20225  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20226  * configured to reference a certain URL.<br><br>
20227  * <p>
20228  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20229  * from which the running page was served.<br><br>
20230  * <p>
20231  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20232  * <p>
20233  * Be aware that to enable the browser to parse an XML document, the server must set
20234  * the Content-Type header in the HTTP response to "text/xml".
20235  * @constructor
20236  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20237  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20238  * will be used to make the request.
20239  */
20240 Roo.data.HttpProxy = function(conn){
20241     Roo.data.HttpProxy.superclass.constructor.call(this);
20242     // is conn a conn config or a real conn?
20243     this.conn = conn;
20244     this.useAjax = !conn || !conn.events;
20245   
20246 };
20247
20248 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20249     // thse are take from connection...
20250     
20251     /**
20252      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20253      */
20254     /**
20255      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20256      * extra parameters to each request made by this object. (defaults to undefined)
20257      */
20258     /**
20259      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20260      *  to each request made by this object. (defaults to undefined)
20261      */
20262     /**
20263      * @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)
20264      */
20265     /**
20266      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20267      */
20268      /**
20269      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20270      * @type Boolean
20271      */
20272   
20273
20274     /**
20275      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20276      * @type Boolean
20277      */
20278     /**
20279      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20280      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20281      * a finer-grained basis than the DataProxy events.
20282      */
20283     getConnection : function(){
20284         return this.useAjax ? Roo.Ajax : this.conn;
20285     },
20286
20287     /**
20288      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20289      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20290      * process that block using the passed callback.
20291      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20292      * for the request to the remote server.
20293      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20294      * object into a block of Roo.data.Records.
20295      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20296      * The function must be passed <ul>
20297      * <li>The Record block object</li>
20298      * <li>The "arg" argument from the load function</li>
20299      * <li>A boolean success indicator</li>
20300      * </ul>
20301      * @param {Object} scope The scope in which to call the callback
20302      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20303      */
20304     load : function(params, reader, callback, scope, arg){
20305         if(this.fireEvent("beforeload", this, params) !== false){
20306             var  o = {
20307                 params : params || {},
20308                 request: {
20309                     callback : callback,
20310                     scope : scope,
20311                     arg : arg
20312                 },
20313                 reader: reader,
20314                 callback : this.loadResponse,
20315                 scope: this
20316             };
20317             if(this.useAjax){
20318                 Roo.applyIf(o, this.conn);
20319                 if(this.activeRequest){
20320                     Roo.Ajax.abort(this.activeRequest);
20321                 }
20322                 this.activeRequest = Roo.Ajax.request(o);
20323             }else{
20324                 this.conn.request(o);
20325             }
20326         }else{
20327             callback.call(scope||this, null, arg, false);
20328         }
20329     },
20330
20331     // private
20332     loadResponse : function(o, success, response){
20333         delete this.activeRequest;
20334         if(!success){
20335             this.fireEvent("loadexception", this, o, response);
20336             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20337             return;
20338         }
20339         var result;
20340         try {
20341             result = o.reader.read(response);
20342         }catch(e){
20343             this.fireEvent("loadexception", this, o, response, e);
20344             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20345             return;
20346         }
20347         
20348         this.fireEvent("load", this, o, o.request.arg);
20349         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20350     },
20351
20352     // private
20353     update : function(dataSet){
20354
20355     },
20356
20357     // private
20358     updateResponse : function(dataSet){
20359
20360     }
20361 });/*
20362  * Based on:
20363  * Ext JS Library 1.1.1
20364  * Copyright(c) 2006-2007, Ext JS, LLC.
20365  *
20366  * Originally Released Under LGPL - original licence link has changed is not relivant.
20367  *
20368  * Fork - LGPL
20369  * <script type="text/javascript">
20370  */
20371
20372 /**
20373  * @class Roo.data.ScriptTagProxy
20374  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20375  * other than the originating domain of the running page.<br><br>
20376  * <p>
20377  * <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
20378  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20379  * <p>
20380  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20381  * source code that is used as the source inside a &lt;script> tag.<br><br>
20382  * <p>
20383  * In order for the browser to process the returned data, the server must wrap the data object
20384  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20385  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20386  * depending on whether the callback name was passed:
20387  * <p>
20388  * <pre><code>
20389 boolean scriptTag = false;
20390 String cb = request.getParameter("callback");
20391 if (cb != null) {
20392     scriptTag = true;
20393     response.setContentType("text/javascript");
20394 } else {
20395     response.setContentType("application/x-json");
20396 }
20397 Writer out = response.getWriter();
20398 if (scriptTag) {
20399     out.write(cb + "(");
20400 }
20401 out.print(dataBlock.toJsonString());
20402 if (scriptTag) {
20403     out.write(");");
20404 }
20405 </pre></code>
20406  *
20407  * @constructor
20408  * @param {Object} config A configuration object.
20409  */
20410 Roo.data.ScriptTagProxy = function(config){
20411     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20412     Roo.apply(this, config);
20413     this.head = document.getElementsByTagName("head")[0];
20414 };
20415
20416 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20417
20418 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20419     /**
20420      * @cfg {String} url The URL from which to request the data object.
20421      */
20422     /**
20423      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20424      */
20425     timeout : 30000,
20426     /**
20427      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20428      * the server the name of the callback function set up by the load call to process the returned data object.
20429      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20430      * javascript output which calls this named function passing the data object as its only parameter.
20431      */
20432     callbackParam : "callback",
20433     /**
20434      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20435      * name to the request.
20436      */
20437     nocache : true,
20438
20439     /**
20440      * Load data from the configured URL, read the data object into
20441      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20442      * process that block using the passed callback.
20443      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20444      * for the request to the remote server.
20445      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20446      * object into a block of Roo.data.Records.
20447      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20448      * The function must be passed <ul>
20449      * <li>The Record block object</li>
20450      * <li>The "arg" argument from the load function</li>
20451      * <li>A boolean success indicator</li>
20452      * </ul>
20453      * @param {Object} scope The scope in which to call the callback
20454      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20455      */
20456     load : function(params, reader, callback, scope, arg){
20457         if(this.fireEvent("beforeload", this, params) !== false){
20458
20459             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20460
20461             var url = this.url;
20462             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20463             if(this.nocache){
20464                 url += "&_dc=" + (new Date().getTime());
20465             }
20466             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20467             var trans = {
20468                 id : transId,
20469                 cb : "stcCallback"+transId,
20470                 scriptId : "stcScript"+transId,
20471                 params : params,
20472                 arg : arg,
20473                 url : url,
20474                 callback : callback,
20475                 scope : scope,
20476                 reader : reader
20477             };
20478             var conn = this;
20479
20480             window[trans.cb] = function(o){
20481                 conn.handleResponse(o, trans);
20482             };
20483
20484             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20485
20486             if(this.autoAbort !== false){
20487                 this.abort();
20488             }
20489
20490             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20491
20492             var script = document.createElement("script");
20493             script.setAttribute("src", url);
20494             script.setAttribute("type", "text/javascript");
20495             script.setAttribute("id", trans.scriptId);
20496             this.head.appendChild(script);
20497
20498             this.trans = trans;
20499         }else{
20500             callback.call(scope||this, null, arg, false);
20501         }
20502     },
20503
20504     // private
20505     isLoading : function(){
20506         return this.trans ? true : false;
20507     },
20508
20509     /**
20510      * Abort the current server request.
20511      */
20512     abort : function(){
20513         if(this.isLoading()){
20514             this.destroyTrans(this.trans);
20515         }
20516     },
20517
20518     // private
20519     destroyTrans : function(trans, isLoaded){
20520         this.head.removeChild(document.getElementById(trans.scriptId));
20521         clearTimeout(trans.timeoutId);
20522         if(isLoaded){
20523             window[trans.cb] = undefined;
20524             try{
20525                 delete window[trans.cb];
20526             }catch(e){}
20527         }else{
20528             // if hasn't been loaded, wait for load to remove it to prevent script error
20529             window[trans.cb] = function(){
20530                 window[trans.cb] = undefined;
20531                 try{
20532                     delete window[trans.cb];
20533                 }catch(e){}
20534             };
20535         }
20536     },
20537
20538     // private
20539     handleResponse : function(o, trans){
20540         this.trans = false;
20541         this.destroyTrans(trans, true);
20542         var result;
20543         try {
20544             result = trans.reader.readRecords(o);
20545         }catch(e){
20546             this.fireEvent("loadexception", this, o, trans.arg, e);
20547             trans.callback.call(trans.scope||window, null, trans.arg, false);
20548             return;
20549         }
20550         this.fireEvent("load", this, o, trans.arg);
20551         trans.callback.call(trans.scope||window, result, trans.arg, true);
20552     },
20553
20554     // private
20555     handleFailure : function(trans){
20556         this.trans = false;
20557         this.destroyTrans(trans, false);
20558         this.fireEvent("loadexception", this, null, trans.arg);
20559         trans.callback.call(trans.scope||window, null, trans.arg, false);
20560     }
20561 });/*
20562  * Based on:
20563  * Ext JS Library 1.1.1
20564  * Copyright(c) 2006-2007, Ext JS, LLC.
20565  *
20566  * Originally Released Under LGPL - original licence link has changed is not relivant.
20567  *
20568  * Fork - LGPL
20569  * <script type="text/javascript">
20570  */
20571
20572 /**
20573  * @class Roo.data.JsonReader
20574  * @extends Roo.data.DataReader
20575  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20576  * based on mappings in a provided Roo.data.Record constructor.
20577  * 
20578  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20579  * in the reply previously. 
20580  * 
20581  * <p>
20582  * Example code:
20583  * <pre><code>
20584 var RecordDef = Roo.data.Record.create([
20585     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20586     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20587 ]);
20588 var myReader = new Roo.data.JsonReader({
20589     totalProperty: "results",    // The property which contains the total dataset size (optional)
20590     root: "rows",                // The property which contains an Array of row objects
20591     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20592 }, RecordDef);
20593 </code></pre>
20594  * <p>
20595  * This would consume a JSON file like this:
20596  * <pre><code>
20597 { 'results': 2, 'rows': [
20598     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20599     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20600 }
20601 </code></pre>
20602  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20603  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20604  * paged from the remote server.
20605  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20606  * @cfg {String} root name of the property which contains the Array of row objects.
20607  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20608  * @constructor
20609  * Create a new JsonReader
20610  * @param {Object} meta Metadata configuration options
20611  * @param {Object} recordType Either an Array of field definition objects,
20612  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20613  */
20614 Roo.data.JsonReader = function(meta, recordType){
20615     
20616     meta = meta || {};
20617     // set some defaults:
20618     Roo.applyIf(meta, {
20619         totalProperty: 'total',
20620         successProperty : 'success',
20621         root : 'data',
20622         id : 'id'
20623     });
20624     
20625     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20626 };
20627 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20628     
20629     /**
20630      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20631      * Used by Store query builder to append _requestMeta to params.
20632      * 
20633      */
20634     metaFromRemote : false,
20635     /**
20636      * This method is only used by a DataProxy which has retrieved data from a remote server.
20637      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20638      * @return {Object} data A data block which is used by an Roo.data.Store object as
20639      * a cache of Roo.data.Records.
20640      */
20641     read : function(response){
20642         var json = response.responseText;
20643        
20644         var o = /* eval:var:o */ eval("("+json+")");
20645         if(!o) {
20646             throw {message: "JsonReader.read: Json object not found"};
20647         }
20648         
20649         if(o.metaData){
20650             
20651             delete this.ef;
20652             this.metaFromRemote = true;
20653             this.meta = o.metaData;
20654             this.recordType = Roo.data.Record.create(o.metaData.fields);
20655             this.onMetaChange(this.meta, this.recordType, o);
20656         }
20657         return this.readRecords(o);
20658     },
20659
20660     // private function a store will implement
20661     onMetaChange : function(meta, recordType, o){
20662
20663     },
20664
20665     /**
20666          * @ignore
20667          */
20668     simpleAccess: function(obj, subsc) {
20669         return obj[subsc];
20670     },
20671
20672         /**
20673          * @ignore
20674          */
20675     getJsonAccessor: function(){
20676         var re = /[\[\.]/;
20677         return function(expr) {
20678             try {
20679                 return(re.test(expr))
20680                     ? new Function("obj", "return obj." + expr)
20681                     : function(obj){
20682                         return obj[expr];
20683                     };
20684             } catch(e){}
20685             return Roo.emptyFn;
20686         };
20687     }(),
20688
20689     /**
20690      * Create a data block containing Roo.data.Records from an XML document.
20691      * @param {Object} o An object which contains an Array of row objects in the property specified
20692      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20693      * which contains the total size of the dataset.
20694      * @return {Object} data A data block which is used by an Roo.data.Store object as
20695      * a cache of Roo.data.Records.
20696      */
20697     readRecords : function(o){
20698         /**
20699          * After any data loads, the raw JSON data is available for further custom processing.
20700          * @type Object
20701          */
20702         this.jsonData = o;
20703         var s = this.meta, Record = this.recordType,
20704             f = Record.prototype.fields, fi = f.items, fl = f.length;
20705
20706 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20707         if (!this.ef) {
20708             if(s.totalProperty) {
20709                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20710                 }
20711                 if(s.successProperty) {
20712                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20713                 }
20714                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20715                 if (s.id) {
20716                         var g = this.getJsonAccessor(s.id);
20717                         this.getId = function(rec) {
20718                                 var r = g(rec);
20719                                 return (r === undefined || r === "") ? null : r;
20720                         };
20721                 } else {
20722                         this.getId = function(){return null;};
20723                 }
20724             this.ef = [];
20725             for(var jj = 0; jj < fl; jj++){
20726                 f = fi[jj];
20727                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20728                 this.ef[jj] = this.getJsonAccessor(map);
20729             }
20730         }
20731
20732         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20733         if(s.totalProperty){
20734             var vt = parseInt(this.getTotal(o), 10);
20735             if(!isNaN(vt)){
20736                 totalRecords = vt;
20737             }
20738         }
20739         if(s.successProperty){
20740             var vs = this.getSuccess(o);
20741             if(vs === false || vs === 'false'){
20742                 success = false;
20743             }
20744         }
20745         var records = [];
20746             for(var i = 0; i < c; i++){
20747                     var n = root[i];
20748                 var values = {};
20749                 var id = this.getId(n);
20750                 for(var j = 0; j < fl; j++){
20751                     f = fi[j];
20752                 var v = this.ef[j](n);
20753                 if (!f.convert) {
20754                     Roo.log('missing convert for ' + f.name);
20755                     Roo.log(f);
20756                     continue;
20757                 }
20758                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20759                 }
20760                 var record = new Record(values, id);
20761                 record.json = n;
20762                 records[i] = record;
20763             }
20764             return {
20765                 success : success,
20766                 records : records,
20767                 totalRecords : totalRecords
20768             };
20769     }
20770 });/*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781 /**
20782  * @class Roo.data.XmlReader
20783  * @extends Roo.data.DataReader
20784  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20785  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20786  * <p>
20787  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20788  * header in the HTTP response must be set to "text/xml".</em>
20789  * <p>
20790  * Example code:
20791  * <pre><code>
20792 var RecordDef = Roo.data.Record.create([
20793    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20794    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20795 ]);
20796 var myReader = new Roo.data.XmlReader({
20797    totalRecords: "results", // The element which contains the total dataset size (optional)
20798    record: "row",           // The repeated element which contains row information
20799    id: "id"                 // The element within the row that provides an ID for the record (optional)
20800 }, RecordDef);
20801 </code></pre>
20802  * <p>
20803  * This would consume an XML file like this:
20804  * <pre><code>
20805 &lt;?xml?>
20806 &lt;dataset>
20807  &lt;results>2&lt;/results>
20808  &lt;row>
20809    &lt;id>1&lt;/id>
20810    &lt;name>Bill&lt;/name>
20811    &lt;occupation>Gardener&lt;/occupation>
20812  &lt;/row>
20813  &lt;row>
20814    &lt;id>2&lt;/id>
20815    &lt;name>Ben&lt;/name>
20816    &lt;occupation>Horticulturalist&lt;/occupation>
20817  &lt;/row>
20818 &lt;/dataset>
20819 </code></pre>
20820  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20821  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20822  * paged from the remote server.
20823  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20824  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20825  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20826  * a record identifier value.
20827  * @constructor
20828  * Create a new XmlReader
20829  * @param {Object} meta Metadata configuration options
20830  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20831  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20832  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20833  */
20834 Roo.data.XmlReader = function(meta, recordType){
20835     meta = meta || {};
20836     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20837 };
20838 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20839     /**
20840      * This method is only used by a DataProxy which has retrieved data from a remote server.
20841          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20842          * to contain a method called 'responseXML' that returns an XML document object.
20843      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20844      * a cache of Roo.data.Records.
20845      */
20846     read : function(response){
20847         var doc = response.responseXML;
20848         if(!doc) {
20849             throw {message: "XmlReader.read: XML Document not available"};
20850         }
20851         return this.readRecords(doc);
20852     },
20853
20854     /**
20855      * Create a data block containing Roo.data.Records from an XML document.
20856          * @param {Object} doc A parsed XML document.
20857      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20858      * a cache of Roo.data.Records.
20859      */
20860     readRecords : function(doc){
20861         /**
20862          * After any data loads/reads, the raw XML Document is available for further custom processing.
20863          * @type XMLDocument
20864          */
20865         this.xmlData = doc;
20866         var root = doc.documentElement || doc;
20867         var q = Roo.DomQuery;
20868         var recordType = this.recordType, fields = recordType.prototype.fields;
20869         var sid = this.meta.id;
20870         var totalRecords = 0, success = true;
20871         if(this.meta.totalRecords){
20872             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20873         }
20874         
20875         if(this.meta.success){
20876             var sv = q.selectValue(this.meta.success, root, true);
20877             success = sv !== false && sv !== 'false';
20878         }
20879         var records = [];
20880         var ns = q.select(this.meta.record, root);
20881         for(var i = 0, len = ns.length; i < len; i++) {
20882                 var n = ns[i];
20883                 var values = {};
20884                 var id = sid ? q.selectValue(sid, n) : undefined;
20885                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20886                     var f = fields.items[j];
20887                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20888                     v = f.convert(v);
20889                     values[f.name] = v;
20890                 }
20891                 var record = new recordType(values, id);
20892                 record.node = n;
20893                 records[records.length] = record;
20894             }
20895
20896             return {
20897                 success : success,
20898                 records : records,
20899                 totalRecords : totalRecords || records.length
20900             };
20901     }
20902 });/*
20903  * Based on:
20904  * Ext JS Library 1.1.1
20905  * Copyright(c) 2006-2007, Ext JS, LLC.
20906  *
20907  * Originally Released Under LGPL - original licence link has changed is not relivant.
20908  *
20909  * Fork - LGPL
20910  * <script type="text/javascript">
20911  */
20912
20913 /**
20914  * @class Roo.data.ArrayReader
20915  * @extends Roo.data.DataReader
20916  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20917  * Each element of that Array represents a row of data fields. The
20918  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20919  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20920  * <p>
20921  * Example code:.
20922  * <pre><code>
20923 var RecordDef = Roo.data.Record.create([
20924     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20925     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20926 ]);
20927 var myReader = new Roo.data.ArrayReader({
20928     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20929 }, RecordDef);
20930 </code></pre>
20931  * <p>
20932  * This would consume an Array like this:
20933  * <pre><code>
20934 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20935   </code></pre>
20936  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20937  * @constructor
20938  * Create a new JsonReader
20939  * @param {Object} meta Metadata configuration options.
20940  * @param {Object} recordType Either an Array of field definition objects
20941  * as specified to {@link Roo.data.Record#create},
20942  * or an {@link Roo.data.Record} object
20943  * created using {@link Roo.data.Record#create}.
20944  */
20945 Roo.data.ArrayReader = function(meta, recordType){
20946     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20947 };
20948
20949 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20950     /**
20951      * Create a data block containing Roo.data.Records from an XML document.
20952      * @param {Object} o An Array of row objects which represents the dataset.
20953      * @return {Object} data A data block which is used by an Roo.data.Store object as
20954      * a cache of Roo.data.Records.
20955      */
20956     readRecords : function(o){
20957         var sid = this.meta ? this.meta.id : null;
20958         var recordType = this.recordType, fields = recordType.prototype.fields;
20959         var records = [];
20960         var root = o;
20961             for(var i = 0; i < root.length; i++){
20962                     var n = root[i];
20963                 var values = {};
20964                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20965                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20966                 var f = fields.items[j];
20967                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20968                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20969                 v = f.convert(v);
20970                 values[f.name] = v;
20971             }
20972                 var record = new recordType(values, id);
20973                 record.json = n;
20974                 records[records.length] = record;
20975             }
20976             return {
20977                 records : records,
20978                 totalRecords : records.length
20979             };
20980     }
20981 });/*
20982  * Based on:
20983  * Ext JS Library 1.1.1
20984  * Copyright(c) 2006-2007, Ext JS, LLC.
20985  *
20986  * Originally Released Under LGPL - original licence link has changed is not relivant.
20987  *
20988  * Fork - LGPL
20989  * <script type="text/javascript">
20990  */
20991
20992
20993 /**
20994  * @class Roo.data.Tree
20995  * @extends Roo.util.Observable
20996  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20997  * in the tree have most standard DOM functionality.
20998  * @constructor
20999  * @param {Node} root (optional) The root node
21000  */
21001 Roo.data.Tree = function(root){
21002    this.nodeHash = {};
21003    /**
21004     * The root node for this tree
21005     * @type Node
21006     */
21007    this.root = null;
21008    if(root){
21009        this.setRootNode(root);
21010    }
21011    this.addEvents({
21012        /**
21013         * @event append
21014         * Fires when a new child node is appended to a node in this tree.
21015         * @param {Tree} tree The owner tree
21016         * @param {Node} parent The parent node
21017         * @param {Node} node The newly appended node
21018         * @param {Number} index The index of the newly appended node
21019         */
21020        "append" : true,
21021        /**
21022         * @event remove
21023         * Fires when a child node is removed from a node in this tree.
21024         * @param {Tree} tree The owner tree
21025         * @param {Node} parent The parent node
21026         * @param {Node} node The child node removed
21027         */
21028        "remove" : true,
21029        /**
21030         * @event move
21031         * Fires when a node is moved to a new location in the tree
21032         * @param {Tree} tree The owner tree
21033         * @param {Node} node The node moved
21034         * @param {Node} oldParent The old parent of this node
21035         * @param {Node} newParent The new parent of this node
21036         * @param {Number} index The index it was moved to
21037         */
21038        "move" : true,
21039        /**
21040         * @event insert
21041         * Fires when a new child node is inserted in a node in this tree.
21042         * @param {Tree} tree The owner tree
21043         * @param {Node} parent The parent node
21044         * @param {Node} node The child node inserted
21045         * @param {Node} refNode The child node the node was inserted before
21046         */
21047        "insert" : true,
21048        /**
21049         * @event beforeappend
21050         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
21051         * @param {Tree} tree The owner tree
21052         * @param {Node} parent The parent node
21053         * @param {Node} node The child node to be appended
21054         */
21055        "beforeappend" : true,
21056        /**
21057         * @event beforeremove
21058         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
21059         * @param {Tree} tree The owner tree
21060         * @param {Node} parent The parent node
21061         * @param {Node} node The child node to be removed
21062         */
21063        "beforeremove" : true,
21064        /**
21065         * @event beforemove
21066         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
21067         * @param {Tree} tree The owner tree
21068         * @param {Node} node The node being moved
21069         * @param {Node} oldParent The parent of the node
21070         * @param {Node} newParent The new parent the node is moving to
21071         * @param {Number} index The index it is being moved to
21072         */
21073        "beforemove" : true,
21074        /**
21075         * @event beforeinsert
21076         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
21077         * @param {Tree} tree The owner tree
21078         * @param {Node} parent The parent node
21079         * @param {Node} node The child node to be inserted
21080         * @param {Node} refNode The child node the node is being inserted before
21081         */
21082        "beforeinsert" : true
21083    });
21084
21085     Roo.data.Tree.superclass.constructor.call(this);
21086 };
21087
21088 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
21089     pathSeparator: "/",
21090
21091     proxyNodeEvent : function(){
21092         return this.fireEvent.apply(this, arguments);
21093     },
21094
21095     /**
21096      * Returns the root node for this tree.
21097      * @return {Node}
21098      */
21099     getRootNode : function(){
21100         return this.root;
21101     },
21102
21103     /**
21104      * Sets the root node for this tree.
21105      * @param {Node} node
21106      * @return {Node}
21107      */
21108     setRootNode : function(node){
21109         this.root = node;
21110         node.ownerTree = this;
21111         node.isRoot = true;
21112         this.registerNode(node);
21113         return node;
21114     },
21115
21116     /**
21117      * Gets a node in this tree by its id.
21118      * @param {String} id
21119      * @return {Node}
21120      */
21121     getNodeById : function(id){
21122         return this.nodeHash[id];
21123     },
21124
21125     registerNode : function(node){
21126         this.nodeHash[node.id] = node;
21127     },
21128
21129     unregisterNode : function(node){
21130         delete this.nodeHash[node.id];
21131     },
21132
21133     toString : function(){
21134         return "[Tree"+(this.id?" "+this.id:"")+"]";
21135     }
21136 });
21137
21138 /**
21139  * @class Roo.data.Node
21140  * @extends Roo.util.Observable
21141  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21142  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21143  * @constructor
21144  * @param {Object} attributes The attributes/config for the node
21145  */
21146 Roo.data.Node = function(attributes){
21147     /**
21148      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21149      * @type {Object}
21150      */
21151     this.attributes = attributes || {};
21152     this.leaf = this.attributes.leaf;
21153     /**
21154      * The node id. @type String
21155      */
21156     this.id = this.attributes.id;
21157     if(!this.id){
21158         this.id = Roo.id(null, "ynode-");
21159         this.attributes.id = this.id;
21160     }
21161     /**
21162      * All child nodes of this node. @type Array
21163      */
21164     this.childNodes = [];
21165     if(!this.childNodes.indexOf){ // indexOf is a must
21166         this.childNodes.indexOf = function(o){
21167             for(var i = 0, len = this.length; i < len; i++){
21168                 if(this[i] == o) {
21169                     return i;
21170                 }
21171             }
21172             return -1;
21173         };
21174     }
21175     /**
21176      * The parent node for this node. @type Node
21177      */
21178     this.parentNode = null;
21179     /**
21180      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21181      */
21182     this.firstChild = null;
21183     /**
21184      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21185      */
21186     this.lastChild = null;
21187     /**
21188      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21189      */
21190     this.previousSibling = null;
21191     /**
21192      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21193      */
21194     this.nextSibling = null;
21195
21196     this.addEvents({
21197        /**
21198         * @event append
21199         * Fires when a new child node is appended
21200         * @param {Tree} tree The owner tree
21201         * @param {Node} this This node
21202         * @param {Node} node The newly appended node
21203         * @param {Number} index The index of the newly appended node
21204         */
21205        "append" : true,
21206        /**
21207         * @event remove
21208         * Fires when a child node is removed
21209         * @param {Tree} tree The owner tree
21210         * @param {Node} this This node
21211         * @param {Node} node The removed node
21212         */
21213        "remove" : true,
21214        /**
21215         * @event move
21216         * Fires when this node is moved to a new location in the tree
21217         * @param {Tree} tree The owner tree
21218         * @param {Node} this This node
21219         * @param {Node} oldParent The old parent of this node
21220         * @param {Node} newParent The new parent of this node
21221         * @param {Number} index The index it was moved to
21222         */
21223        "move" : true,
21224        /**
21225         * @event insert
21226         * Fires when a new child node is inserted.
21227         * @param {Tree} tree The owner tree
21228         * @param {Node} this This node
21229         * @param {Node} node The child node inserted
21230         * @param {Node} refNode The child node the node was inserted before
21231         */
21232        "insert" : true,
21233        /**
21234         * @event beforeappend
21235         * Fires before a new child is appended, return false to cancel the append.
21236         * @param {Tree} tree The owner tree
21237         * @param {Node} this This node
21238         * @param {Node} node The child node to be appended
21239         */
21240        "beforeappend" : true,
21241        /**
21242         * @event beforeremove
21243         * Fires before a child is removed, return false to cancel the remove.
21244         * @param {Tree} tree The owner tree
21245         * @param {Node} this This node
21246         * @param {Node} node The child node to be removed
21247         */
21248        "beforeremove" : true,
21249        /**
21250         * @event beforemove
21251         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21252         * @param {Tree} tree The owner tree
21253         * @param {Node} this This node
21254         * @param {Node} oldParent The parent of this node
21255         * @param {Node} newParent The new parent this node is moving to
21256         * @param {Number} index The index it is being moved to
21257         */
21258        "beforemove" : true,
21259        /**
21260         * @event beforeinsert
21261         * Fires before a new child is inserted, return false to cancel the insert.
21262         * @param {Tree} tree The owner tree
21263         * @param {Node} this This node
21264         * @param {Node} node The child node to be inserted
21265         * @param {Node} refNode The child node the node is being inserted before
21266         */
21267        "beforeinsert" : true
21268    });
21269     this.listeners = this.attributes.listeners;
21270     Roo.data.Node.superclass.constructor.call(this);
21271 };
21272
21273 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21274     fireEvent : function(evtName){
21275         // first do standard event for this node
21276         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21277             return false;
21278         }
21279         // then bubble it up to the tree if the event wasn't cancelled
21280         var ot = this.getOwnerTree();
21281         if(ot){
21282             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21283                 return false;
21284             }
21285         }
21286         return true;
21287     },
21288
21289     /**
21290      * Returns true if this node is a leaf
21291      * @return {Boolean}
21292      */
21293     isLeaf : function(){
21294         return this.leaf === true;
21295     },
21296
21297     // private
21298     setFirstChild : function(node){
21299         this.firstChild = node;
21300     },
21301
21302     //private
21303     setLastChild : function(node){
21304         this.lastChild = node;
21305     },
21306
21307
21308     /**
21309      * Returns true if this node is the last child of its parent
21310      * @return {Boolean}
21311      */
21312     isLast : function(){
21313        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21314     },
21315
21316     /**
21317      * Returns true if this node is the first child of its parent
21318      * @return {Boolean}
21319      */
21320     isFirst : function(){
21321        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21322     },
21323
21324     hasChildNodes : function(){
21325         return !this.isLeaf() && this.childNodes.length > 0;
21326     },
21327
21328     /**
21329      * Insert node(s) as the last child node of this node.
21330      * @param {Node/Array} node The node or Array of nodes to append
21331      * @return {Node} The appended node if single append, or null if an array was passed
21332      */
21333     appendChild : function(node){
21334         var multi = false;
21335         if(node instanceof Array){
21336             multi = node;
21337         }else if(arguments.length > 1){
21338             multi = arguments;
21339         }
21340         // if passed an array or multiple args do them one by one
21341         if(multi){
21342             for(var i = 0, len = multi.length; i < len; i++) {
21343                 this.appendChild(multi[i]);
21344             }
21345         }else{
21346             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21347                 return false;
21348             }
21349             var index = this.childNodes.length;
21350             var oldParent = node.parentNode;
21351             // it's a move, make sure we move it cleanly
21352             if(oldParent){
21353                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21354                     return false;
21355                 }
21356                 oldParent.removeChild(node);
21357             }
21358             index = this.childNodes.length;
21359             if(index == 0){
21360                 this.setFirstChild(node);
21361             }
21362             this.childNodes.push(node);
21363             node.parentNode = this;
21364             var ps = this.childNodes[index-1];
21365             if(ps){
21366                 node.previousSibling = ps;
21367                 ps.nextSibling = node;
21368             }else{
21369                 node.previousSibling = null;
21370             }
21371             node.nextSibling = null;
21372             this.setLastChild(node);
21373             node.setOwnerTree(this.getOwnerTree());
21374             this.fireEvent("append", this.ownerTree, this, node, index);
21375             if(oldParent){
21376                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21377             }
21378             return node;
21379         }
21380     },
21381
21382     /**
21383      * Removes a child node from this node.
21384      * @param {Node} node The node to remove
21385      * @return {Node} The removed node
21386      */
21387     removeChild : function(node){
21388         var index = this.childNodes.indexOf(node);
21389         if(index == -1){
21390             return false;
21391         }
21392         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21393             return false;
21394         }
21395
21396         // remove it from childNodes collection
21397         this.childNodes.splice(index, 1);
21398
21399         // update siblings
21400         if(node.previousSibling){
21401             node.previousSibling.nextSibling = node.nextSibling;
21402         }
21403         if(node.nextSibling){
21404             node.nextSibling.previousSibling = node.previousSibling;
21405         }
21406
21407         // update child refs
21408         if(this.firstChild == node){
21409             this.setFirstChild(node.nextSibling);
21410         }
21411         if(this.lastChild == node){
21412             this.setLastChild(node.previousSibling);
21413         }
21414
21415         node.setOwnerTree(null);
21416         // clear any references from the node
21417         node.parentNode = null;
21418         node.previousSibling = null;
21419         node.nextSibling = null;
21420         this.fireEvent("remove", this.ownerTree, this, node);
21421         return node;
21422     },
21423
21424     /**
21425      * Inserts the first node before the second node in this nodes childNodes collection.
21426      * @param {Node} node The node to insert
21427      * @param {Node} refNode The node to insert before (if null the node is appended)
21428      * @return {Node} The inserted node
21429      */
21430     insertBefore : function(node, refNode){
21431         if(!refNode){ // like standard Dom, refNode can be null for append
21432             return this.appendChild(node);
21433         }
21434         // nothing to do
21435         if(node == refNode){
21436             return false;
21437         }
21438
21439         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21440             return false;
21441         }
21442         var index = this.childNodes.indexOf(refNode);
21443         var oldParent = node.parentNode;
21444         var refIndex = index;
21445
21446         // when moving internally, indexes will change after remove
21447         if(oldParent == this && this.childNodes.indexOf(node) < index){
21448             refIndex--;
21449         }
21450
21451         // it's a move, make sure we move it cleanly
21452         if(oldParent){
21453             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21454                 return false;
21455             }
21456             oldParent.removeChild(node);
21457         }
21458         if(refIndex == 0){
21459             this.setFirstChild(node);
21460         }
21461         this.childNodes.splice(refIndex, 0, node);
21462         node.parentNode = this;
21463         var ps = this.childNodes[refIndex-1];
21464         if(ps){
21465             node.previousSibling = ps;
21466             ps.nextSibling = node;
21467         }else{
21468             node.previousSibling = null;
21469         }
21470         node.nextSibling = refNode;
21471         refNode.previousSibling = node;
21472         node.setOwnerTree(this.getOwnerTree());
21473         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21474         if(oldParent){
21475             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21476         }
21477         return node;
21478     },
21479
21480     /**
21481      * Returns the child node at the specified index.
21482      * @param {Number} index
21483      * @return {Node}
21484      */
21485     item : function(index){
21486         return this.childNodes[index];
21487     },
21488
21489     /**
21490      * Replaces one child node in this node with another.
21491      * @param {Node} newChild The replacement node
21492      * @param {Node} oldChild The node to replace
21493      * @return {Node} The replaced node
21494      */
21495     replaceChild : function(newChild, oldChild){
21496         this.insertBefore(newChild, oldChild);
21497         this.removeChild(oldChild);
21498         return oldChild;
21499     },
21500
21501     /**
21502      * Returns the index of a child node
21503      * @param {Node} node
21504      * @return {Number} The index of the node or -1 if it was not found
21505      */
21506     indexOf : function(child){
21507         return this.childNodes.indexOf(child);
21508     },
21509
21510     /**
21511      * Returns the tree this node is in.
21512      * @return {Tree}
21513      */
21514     getOwnerTree : function(){
21515         // if it doesn't have one, look for one
21516         if(!this.ownerTree){
21517             var p = this;
21518             while(p){
21519                 if(p.ownerTree){
21520                     this.ownerTree = p.ownerTree;
21521                     break;
21522                 }
21523                 p = p.parentNode;
21524             }
21525         }
21526         return this.ownerTree;
21527     },
21528
21529     /**
21530      * Returns depth of this node (the root node has a depth of 0)
21531      * @return {Number}
21532      */
21533     getDepth : function(){
21534         var depth = 0;
21535         var p = this;
21536         while(p.parentNode){
21537             ++depth;
21538             p = p.parentNode;
21539         }
21540         return depth;
21541     },
21542
21543     // private
21544     setOwnerTree : function(tree){
21545         // if it's move, we need to update everyone
21546         if(tree != this.ownerTree){
21547             if(this.ownerTree){
21548                 this.ownerTree.unregisterNode(this);
21549             }
21550             this.ownerTree = tree;
21551             var cs = this.childNodes;
21552             for(var i = 0, len = cs.length; i < len; i++) {
21553                 cs[i].setOwnerTree(tree);
21554             }
21555             if(tree){
21556                 tree.registerNode(this);
21557             }
21558         }
21559     },
21560
21561     /**
21562      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21563      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21564      * @return {String} The path
21565      */
21566     getPath : function(attr){
21567         attr = attr || "id";
21568         var p = this.parentNode;
21569         var b = [this.attributes[attr]];
21570         while(p){
21571             b.unshift(p.attributes[attr]);
21572             p = p.parentNode;
21573         }
21574         var sep = this.getOwnerTree().pathSeparator;
21575         return sep + b.join(sep);
21576     },
21577
21578     /**
21579      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21580      * function call will be the scope provided or the current node. The arguments to the function
21581      * will be the args provided or the current node. If the function returns false at any point,
21582      * the bubble is stopped.
21583      * @param {Function} fn The function to call
21584      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21585      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21586      */
21587     bubble : function(fn, scope, args){
21588         var p = this;
21589         while(p){
21590             if(fn.call(scope || p, args || p) === false){
21591                 break;
21592             }
21593             p = p.parentNode;
21594         }
21595     },
21596
21597     /**
21598      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21599      * function call will be the scope provided or the current node. The arguments to the function
21600      * will be the args provided or the current node. If the function returns false at any point,
21601      * the cascade is stopped on that branch.
21602      * @param {Function} fn The function to call
21603      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21604      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21605      */
21606     cascade : function(fn, scope, args){
21607         if(fn.call(scope || this, args || this) !== false){
21608             var cs = this.childNodes;
21609             for(var i = 0, len = cs.length; i < len; i++) {
21610                 cs[i].cascade(fn, scope, args);
21611             }
21612         }
21613     },
21614
21615     /**
21616      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21617      * function call will be the scope provided or the current node. The arguments to the function
21618      * will be the args provided or the current node. If the function returns false at any point,
21619      * the iteration stops.
21620      * @param {Function} fn The function to call
21621      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21622      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21623      */
21624     eachChild : function(fn, scope, args){
21625         var cs = this.childNodes;
21626         for(var i = 0, len = cs.length; i < len; i++) {
21627                 if(fn.call(scope || this, args || cs[i]) === false){
21628                     break;
21629                 }
21630         }
21631     },
21632
21633     /**
21634      * Finds the first child that has the attribute with the specified value.
21635      * @param {String} attribute The attribute name
21636      * @param {Mixed} value The value to search for
21637      * @return {Node} The found child or null if none was found
21638      */
21639     findChild : function(attribute, value){
21640         var cs = this.childNodes;
21641         for(var i = 0, len = cs.length; i < len; i++) {
21642                 if(cs[i].attributes[attribute] == value){
21643                     return cs[i];
21644                 }
21645         }
21646         return null;
21647     },
21648
21649     /**
21650      * Finds the first child by a custom function. The child matches if the function passed
21651      * returns true.
21652      * @param {Function} fn
21653      * @param {Object} scope (optional)
21654      * @return {Node} The found child or null if none was found
21655      */
21656     findChildBy : function(fn, scope){
21657         var cs = this.childNodes;
21658         for(var i = 0, len = cs.length; i < len; i++) {
21659                 if(fn.call(scope||cs[i], cs[i]) === true){
21660                     return cs[i];
21661                 }
21662         }
21663         return null;
21664     },
21665
21666     /**
21667      * Sorts this nodes children using the supplied sort function
21668      * @param {Function} fn
21669      * @param {Object} scope (optional)
21670      */
21671     sort : function(fn, scope){
21672         var cs = this.childNodes;
21673         var len = cs.length;
21674         if(len > 0){
21675             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21676             cs.sort(sortFn);
21677             for(var i = 0; i < len; i++){
21678                 var n = cs[i];
21679                 n.previousSibling = cs[i-1];
21680                 n.nextSibling = cs[i+1];
21681                 if(i == 0){
21682                     this.setFirstChild(n);
21683                 }
21684                 if(i == len-1){
21685                     this.setLastChild(n);
21686                 }
21687             }
21688         }
21689     },
21690
21691     /**
21692      * Returns true if this node is an ancestor (at any point) of the passed node.
21693      * @param {Node} node
21694      * @return {Boolean}
21695      */
21696     contains : function(node){
21697         return node.isAncestor(this);
21698     },
21699
21700     /**
21701      * Returns true if the passed node is an ancestor (at any point) of this node.
21702      * @param {Node} node
21703      * @return {Boolean}
21704      */
21705     isAncestor : function(node){
21706         var p = this.parentNode;
21707         while(p){
21708             if(p == node){
21709                 return true;
21710             }
21711             p = p.parentNode;
21712         }
21713         return false;
21714     },
21715
21716     toString : function(){
21717         return "[Node"+(this.id?" "+this.id:"")+"]";
21718     }
21719 });/*
21720  * Based on:
21721  * Ext JS Library 1.1.1
21722  * Copyright(c) 2006-2007, Ext JS, LLC.
21723  *
21724  * Originally Released Under LGPL - original licence link has changed is not relivant.
21725  *
21726  * Fork - LGPL
21727  * <script type="text/javascript">
21728  */
21729  
21730
21731 /**
21732  * @class Roo.ComponentMgr
21733  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21734  * @singleton
21735  */
21736 Roo.ComponentMgr = function(){
21737     var all = new Roo.util.MixedCollection();
21738
21739     return {
21740         /**
21741          * Registers a component.
21742          * @param {Roo.Component} c The component
21743          */
21744         register : function(c){
21745             all.add(c);
21746         },
21747
21748         /**
21749          * Unregisters a component.
21750          * @param {Roo.Component} c The component
21751          */
21752         unregister : function(c){
21753             all.remove(c);
21754         },
21755
21756         /**
21757          * Returns a component by id
21758          * @param {String} id The component id
21759          */
21760         get : function(id){
21761             return all.get(id);
21762         },
21763
21764         /**
21765          * Registers a function that will be called when a specified component is added to ComponentMgr
21766          * @param {String} id The component id
21767          * @param {Funtction} fn The callback function
21768          * @param {Object} scope The scope of the callback
21769          */
21770         onAvailable : function(id, fn, scope){
21771             all.on("add", function(index, o){
21772                 if(o.id == id){
21773                     fn.call(scope || o, o);
21774                     all.un("add", fn, scope);
21775                 }
21776             });
21777         }
21778     };
21779 }();/*
21780  * Based on:
21781  * Ext JS Library 1.1.1
21782  * Copyright(c) 2006-2007, Ext JS, LLC.
21783  *
21784  * Originally Released Under LGPL - original licence link has changed is not relivant.
21785  *
21786  * Fork - LGPL
21787  * <script type="text/javascript">
21788  */
21789  
21790 /**
21791  * @class Roo.Component
21792  * @extends Roo.util.Observable
21793  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21794  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21795  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21796  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21797  * All visual components (widgets) that require rendering into a layout should subclass Component.
21798  * @constructor
21799  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21800  * 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
21801  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21802  */
21803 Roo.Component = function(config){
21804     config = config || {};
21805     if(config.tagName || config.dom || typeof config == "string"){ // element object
21806         config = {el: config, id: config.id || config};
21807     }
21808     this.initialConfig = config;
21809
21810     Roo.apply(this, config);
21811     this.addEvents({
21812         /**
21813          * @event disable
21814          * Fires after the component is disabled.
21815              * @param {Roo.Component} this
21816              */
21817         disable : true,
21818         /**
21819          * @event enable
21820          * Fires after the component is enabled.
21821              * @param {Roo.Component} this
21822              */
21823         enable : true,
21824         /**
21825          * @event beforeshow
21826          * Fires before the component is shown.  Return false to stop the show.
21827              * @param {Roo.Component} this
21828              */
21829         beforeshow : true,
21830         /**
21831          * @event show
21832          * Fires after the component is shown.
21833              * @param {Roo.Component} this
21834              */
21835         show : true,
21836         /**
21837          * @event beforehide
21838          * Fires before the component is hidden. Return false to stop the hide.
21839              * @param {Roo.Component} this
21840              */
21841         beforehide : true,
21842         /**
21843          * @event hide
21844          * Fires after the component is hidden.
21845              * @param {Roo.Component} this
21846              */
21847         hide : true,
21848         /**
21849          * @event beforerender
21850          * Fires before the component is rendered. Return false to stop the render.
21851              * @param {Roo.Component} this
21852              */
21853         beforerender : true,
21854         /**
21855          * @event render
21856          * Fires after the component is rendered.
21857              * @param {Roo.Component} this
21858              */
21859         render : true,
21860         /**
21861          * @event beforedestroy
21862          * Fires before the component is destroyed. Return false to stop the destroy.
21863              * @param {Roo.Component} this
21864              */
21865         beforedestroy : true,
21866         /**
21867          * @event destroy
21868          * Fires after the component is destroyed.
21869              * @param {Roo.Component} this
21870              */
21871         destroy : true
21872     });
21873     if(!this.id){
21874         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21875     }
21876     Roo.ComponentMgr.register(this);
21877     Roo.Component.superclass.constructor.call(this);
21878     this.initComponent();
21879     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21880         this.render(this.renderTo);
21881         delete this.renderTo;
21882     }
21883 };
21884
21885 /** @private */
21886 Roo.Component.AUTO_ID = 1000;
21887
21888 Roo.extend(Roo.Component, Roo.util.Observable, {
21889     /**
21890      * @scope Roo.Component.prototype
21891      * @type {Boolean}
21892      * true if this component is hidden. Read-only.
21893      */
21894     hidden : false,
21895     /**
21896      * @type {Boolean}
21897      * true if this component is disabled. Read-only.
21898      */
21899     disabled : false,
21900     /**
21901      * @type {Boolean}
21902      * true if this component has been rendered. Read-only.
21903      */
21904     rendered : false,
21905     
21906     /** @cfg {String} disableClass
21907      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21908      */
21909     disabledClass : "x-item-disabled",
21910         /** @cfg {Boolean} allowDomMove
21911          * Whether the component can move the Dom node when rendering (defaults to true).
21912          */
21913     allowDomMove : true,
21914     /** @cfg {String} hideMode
21915      * How this component should hidden. Supported values are
21916      * "visibility" (css visibility), "offsets" (negative offset position) and
21917      * "display" (css display) - defaults to "display".
21918      */
21919     hideMode: 'display',
21920
21921     /** @private */
21922     ctype : "Roo.Component",
21923
21924     /**
21925      * @cfg {String} actionMode 
21926      * which property holds the element that used for  hide() / show() / disable() / enable()
21927      * default is 'el' 
21928      */
21929     actionMode : "el",
21930
21931     /** @private */
21932     getActionEl : function(){
21933         return this[this.actionMode];
21934     },
21935
21936     initComponent : Roo.emptyFn,
21937     /**
21938      * If this is a lazy rendering component, render it to its container element.
21939      * @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.
21940      */
21941     render : function(container, position){
21942         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21943             if(!container && this.el){
21944                 this.el = Roo.get(this.el);
21945                 container = this.el.dom.parentNode;
21946                 this.allowDomMove = false;
21947             }
21948             this.container = Roo.get(container);
21949             this.rendered = true;
21950             if(position !== undefined){
21951                 if(typeof position == 'number'){
21952                     position = this.container.dom.childNodes[position];
21953                 }else{
21954                     position = Roo.getDom(position);
21955                 }
21956             }
21957             this.onRender(this.container, position || null);
21958             if(this.cls){
21959                 this.el.addClass(this.cls);
21960                 delete this.cls;
21961             }
21962             if(this.style){
21963                 this.el.applyStyles(this.style);
21964                 delete this.style;
21965             }
21966             this.fireEvent("render", this);
21967             this.afterRender(this.container);
21968             if(this.hidden){
21969                 this.hide();
21970             }
21971             if(this.disabled){
21972                 this.disable();
21973             }
21974         }
21975         return this;
21976     },
21977
21978     /** @private */
21979     // default function is not really useful
21980     onRender : function(ct, position){
21981         if(this.el){
21982             this.el = Roo.get(this.el);
21983             if(this.allowDomMove !== false){
21984                 ct.dom.insertBefore(this.el.dom, position);
21985             }
21986         }
21987     },
21988
21989     /** @private */
21990     getAutoCreate : function(){
21991         var cfg = typeof this.autoCreate == "object" ?
21992                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21993         if(this.id && !cfg.id){
21994             cfg.id = this.id;
21995         }
21996         return cfg;
21997     },
21998
21999     /** @private */
22000     afterRender : Roo.emptyFn,
22001
22002     /**
22003      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
22004      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
22005      */
22006     destroy : function(){
22007         if(this.fireEvent("beforedestroy", this) !== false){
22008             this.purgeListeners();
22009             this.beforeDestroy();
22010             if(this.rendered){
22011                 this.el.removeAllListeners();
22012                 this.el.remove();
22013                 if(this.actionMode == "container"){
22014                     this.container.remove();
22015                 }
22016             }
22017             this.onDestroy();
22018             Roo.ComponentMgr.unregister(this);
22019             this.fireEvent("destroy", this);
22020         }
22021     },
22022
22023         /** @private */
22024     beforeDestroy : function(){
22025
22026     },
22027
22028         /** @private */
22029         onDestroy : function(){
22030
22031     },
22032
22033     /**
22034      * Returns the underlying {@link Roo.Element}.
22035      * @return {Roo.Element} The element
22036      */
22037     getEl : function(){
22038         return this.el;
22039     },
22040
22041     /**
22042      * Returns the id of this component.
22043      * @return {String}
22044      */
22045     getId : function(){
22046         return this.id;
22047     },
22048
22049     /**
22050      * Try to focus this component.
22051      * @param {Boolean} selectText True to also select the text in this component (if applicable)
22052      * @return {Roo.Component} this
22053      */
22054     focus : function(selectText){
22055         if(this.rendered){
22056             this.el.focus();
22057             if(selectText === true){
22058                 this.el.dom.select();
22059             }
22060         }
22061         return this;
22062     },
22063
22064     /** @private */
22065     blur : function(){
22066         if(this.rendered){
22067             this.el.blur();
22068         }
22069         return this;
22070     },
22071
22072     /**
22073      * Disable this component.
22074      * @return {Roo.Component} this
22075      */
22076     disable : function(){
22077         if(this.rendered){
22078             this.onDisable();
22079         }
22080         this.disabled = true;
22081         this.fireEvent("disable", this);
22082         return this;
22083     },
22084
22085         // private
22086     onDisable : function(){
22087         this.getActionEl().addClass(this.disabledClass);
22088         this.el.dom.disabled = true;
22089     },
22090
22091     /**
22092      * Enable this component.
22093      * @return {Roo.Component} this
22094      */
22095     enable : function(){
22096         if(this.rendered){
22097             this.onEnable();
22098         }
22099         this.disabled = false;
22100         this.fireEvent("enable", this);
22101         return this;
22102     },
22103
22104         // private
22105     onEnable : function(){
22106         this.getActionEl().removeClass(this.disabledClass);
22107         this.el.dom.disabled = false;
22108     },
22109
22110     /**
22111      * Convenience function for setting disabled/enabled by boolean.
22112      * @param {Boolean} disabled
22113      */
22114     setDisabled : function(disabled){
22115         this[disabled ? "disable" : "enable"]();
22116     },
22117
22118     /**
22119      * Show this component.
22120      * @return {Roo.Component} this
22121      */
22122     show: function(){
22123         if(this.fireEvent("beforeshow", this) !== false){
22124             this.hidden = false;
22125             if(this.rendered){
22126                 this.onShow();
22127             }
22128             this.fireEvent("show", this);
22129         }
22130         return this;
22131     },
22132
22133     // private
22134     onShow : function(){
22135         var ae = this.getActionEl();
22136         if(this.hideMode == 'visibility'){
22137             ae.dom.style.visibility = "visible";
22138         }else if(this.hideMode == 'offsets'){
22139             ae.removeClass('x-hidden');
22140         }else{
22141             ae.dom.style.display = "";
22142         }
22143     },
22144
22145     /**
22146      * Hide this component.
22147      * @return {Roo.Component} this
22148      */
22149     hide: function(){
22150         if(this.fireEvent("beforehide", this) !== false){
22151             this.hidden = true;
22152             if(this.rendered){
22153                 this.onHide();
22154             }
22155             this.fireEvent("hide", this);
22156         }
22157         return this;
22158     },
22159
22160     // private
22161     onHide : function(){
22162         var ae = this.getActionEl();
22163         if(this.hideMode == 'visibility'){
22164             ae.dom.style.visibility = "hidden";
22165         }else if(this.hideMode == 'offsets'){
22166             ae.addClass('x-hidden');
22167         }else{
22168             ae.dom.style.display = "none";
22169         }
22170     },
22171
22172     /**
22173      * Convenience function to hide or show this component by boolean.
22174      * @param {Boolean} visible True to show, false to hide
22175      * @return {Roo.Component} this
22176      */
22177     setVisible: function(visible){
22178         if(visible) {
22179             this.show();
22180         }else{
22181             this.hide();
22182         }
22183         return this;
22184     },
22185
22186     /**
22187      * Returns true if this component is visible.
22188      */
22189     isVisible : function(){
22190         return this.getActionEl().isVisible();
22191     },
22192
22193     cloneConfig : function(overrides){
22194         overrides = overrides || {};
22195         var id = overrides.id || Roo.id();
22196         var cfg = Roo.applyIf(overrides, this.initialConfig);
22197         cfg.id = id; // prevent dup id
22198         return new this.constructor(cfg);
22199     }
22200 });/*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210  (function(){ 
22211 /**
22212  * @class Roo.Layer
22213  * @extends Roo.Element
22214  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22215  * automatic maintaining of shadow/shim positions.
22216  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22217  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22218  * you can pass a string with a CSS class name. False turns off the shadow.
22219  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22220  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22221  * @cfg {String} cls CSS class to add to the element
22222  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22223  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22224  * @constructor
22225  * @param {Object} config An object with config options.
22226  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22227  */
22228
22229 Roo.Layer = function(config, existingEl){
22230     config = config || {};
22231     var dh = Roo.DomHelper;
22232     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22233     if(existingEl){
22234         this.dom = Roo.getDom(existingEl);
22235     }
22236     if(!this.dom){
22237         var o = config.dh || {tag: "div", cls: "x-layer"};
22238         this.dom = dh.append(pel, o);
22239     }
22240     if(config.cls){
22241         this.addClass(config.cls);
22242     }
22243     this.constrain = config.constrain !== false;
22244     this.visibilityMode = Roo.Element.VISIBILITY;
22245     if(config.id){
22246         this.id = this.dom.id = config.id;
22247     }else{
22248         this.id = Roo.id(this.dom);
22249     }
22250     this.zindex = config.zindex || this.getZIndex();
22251     this.position("absolute", this.zindex);
22252     if(config.shadow){
22253         this.shadowOffset = config.shadowOffset || 4;
22254         this.shadow = new Roo.Shadow({
22255             offset : this.shadowOffset,
22256             mode : config.shadow
22257         });
22258     }else{
22259         this.shadowOffset = 0;
22260     }
22261     this.useShim = config.shim !== false && Roo.useShims;
22262     this.useDisplay = config.useDisplay;
22263     this.hide();
22264 };
22265
22266 var supr = Roo.Element.prototype;
22267
22268 // shims are shared among layer to keep from having 100 iframes
22269 var shims = [];
22270
22271 Roo.extend(Roo.Layer, Roo.Element, {
22272
22273     getZIndex : function(){
22274         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22275     },
22276
22277     getShim : function(){
22278         if(!this.useShim){
22279             return null;
22280         }
22281         if(this.shim){
22282             return this.shim;
22283         }
22284         var shim = shims.shift();
22285         if(!shim){
22286             shim = this.createShim();
22287             shim.enableDisplayMode('block');
22288             shim.dom.style.display = 'none';
22289             shim.dom.style.visibility = 'visible';
22290         }
22291         var pn = this.dom.parentNode;
22292         if(shim.dom.parentNode != pn){
22293             pn.insertBefore(shim.dom, this.dom);
22294         }
22295         shim.setStyle('z-index', this.getZIndex()-2);
22296         this.shim = shim;
22297         return shim;
22298     },
22299
22300     hideShim : function(){
22301         if(this.shim){
22302             this.shim.setDisplayed(false);
22303             shims.push(this.shim);
22304             delete this.shim;
22305         }
22306     },
22307
22308     disableShadow : function(){
22309         if(this.shadow){
22310             this.shadowDisabled = true;
22311             this.shadow.hide();
22312             this.lastShadowOffset = this.shadowOffset;
22313             this.shadowOffset = 0;
22314         }
22315     },
22316
22317     enableShadow : function(show){
22318         if(this.shadow){
22319             this.shadowDisabled = false;
22320             this.shadowOffset = this.lastShadowOffset;
22321             delete this.lastShadowOffset;
22322             if(show){
22323                 this.sync(true);
22324             }
22325         }
22326     },
22327
22328     // private
22329     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22330     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22331     sync : function(doShow){
22332         var sw = this.shadow;
22333         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22334             var sh = this.getShim();
22335
22336             var w = this.getWidth(),
22337                 h = this.getHeight();
22338
22339             var l = this.getLeft(true),
22340                 t = this.getTop(true);
22341
22342             if(sw && !this.shadowDisabled){
22343                 if(doShow && !sw.isVisible()){
22344                     sw.show(this);
22345                 }else{
22346                     sw.realign(l, t, w, h);
22347                 }
22348                 if(sh){
22349                     if(doShow){
22350                        sh.show();
22351                     }
22352                     // fit the shim behind the shadow, so it is shimmed too
22353                     var a = sw.adjusts, s = sh.dom.style;
22354                     s.left = (Math.min(l, l+a.l))+"px";
22355                     s.top = (Math.min(t, t+a.t))+"px";
22356                     s.width = (w+a.w)+"px";
22357                     s.height = (h+a.h)+"px";
22358                 }
22359             }else if(sh){
22360                 if(doShow){
22361                    sh.show();
22362                 }
22363                 sh.setSize(w, h);
22364                 sh.setLeftTop(l, t);
22365             }
22366             
22367         }
22368     },
22369
22370     // private
22371     destroy : function(){
22372         this.hideShim();
22373         if(this.shadow){
22374             this.shadow.hide();
22375         }
22376         this.removeAllListeners();
22377         var pn = this.dom.parentNode;
22378         if(pn){
22379             pn.removeChild(this.dom);
22380         }
22381         Roo.Element.uncache(this.id);
22382     },
22383
22384     remove : function(){
22385         this.destroy();
22386     },
22387
22388     // private
22389     beginUpdate : function(){
22390         this.updating = true;
22391     },
22392
22393     // private
22394     endUpdate : function(){
22395         this.updating = false;
22396         this.sync(true);
22397     },
22398
22399     // private
22400     hideUnders : function(negOffset){
22401         if(this.shadow){
22402             this.shadow.hide();
22403         }
22404         this.hideShim();
22405     },
22406
22407     // private
22408     constrainXY : function(){
22409         if(this.constrain){
22410             var vw = Roo.lib.Dom.getViewWidth(),
22411                 vh = Roo.lib.Dom.getViewHeight();
22412             var s = Roo.get(document).getScroll();
22413
22414             var xy = this.getXY();
22415             var x = xy[0], y = xy[1];   
22416             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22417             // only move it if it needs it
22418             var moved = false;
22419             // first validate right/bottom
22420             if((x + w) > vw+s.left){
22421                 x = vw - w - this.shadowOffset;
22422                 moved = true;
22423             }
22424             if((y + h) > vh+s.top){
22425                 y = vh - h - this.shadowOffset;
22426                 moved = true;
22427             }
22428             // then make sure top/left isn't negative
22429             if(x < s.left){
22430                 x = s.left;
22431                 moved = true;
22432             }
22433             if(y < s.top){
22434                 y = s.top;
22435                 moved = true;
22436             }
22437             if(moved){
22438                 if(this.avoidY){
22439                     var ay = this.avoidY;
22440                     if(y <= ay && (y+h) >= ay){
22441                         y = ay-h-5;   
22442                     }
22443                 }
22444                 xy = [x, y];
22445                 this.storeXY(xy);
22446                 supr.setXY.call(this, xy);
22447                 this.sync();
22448             }
22449         }
22450     },
22451
22452     isVisible : function(){
22453         return this.visible;    
22454     },
22455
22456     // private
22457     showAction : function(){
22458         this.visible = true; // track visibility to prevent getStyle calls
22459         if(this.useDisplay === true){
22460             this.setDisplayed("");
22461         }else if(this.lastXY){
22462             supr.setXY.call(this, this.lastXY);
22463         }else if(this.lastLT){
22464             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22465         }
22466     },
22467
22468     // private
22469     hideAction : function(){
22470         this.visible = false;
22471         if(this.useDisplay === true){
22472             this.setDisplayed(false);
22473         }else{
22474             this.setLeftTop(-10000,-10000);
22475         }
22476     },
22477
22478     // overridden Element method
22479     setVisible : function(v, a, d, c, e){
22480         if(v){
22481             this.showAction();
22482         }
22483         if(a && v){
22484             var cb = function(){
22485                 this.sync(true);
22486                 if(c){
22487                     c();
22488                 }
22489             }.createDelegate(this);
22490             supr.setVisible.call(this, true, true, d, cb, e);
22491         }else{
22492             if(!v){
22493                 this.hideUnders(true);
22494             }
22495             var cb = c;
22496             if(a){
22497                 cb = function(){
22498                     this.hideAction();
22499                     if(c){
22500                         c();
22501                     }
22502                 }.createDelegate(this);
22503             }
22504             supr.setVisible.call(this, v, a, d, cb, e);
22505             if(v){
22506                 this.sync(true);
22507             }else if(!a){
22508                 this.hideAction();
22509             }
22510         }
22511     },
22512
22513     storeXY : function(xy){
22514         delete this.lastLT;
22515         this.lastXY = xy;
22516     },
22517
22518     storeLeftTop : function(left, top){
22519         delete this.lastXY;
22520         this.lastLT = [left, top];
22521     },
22522
22523     // private
22524     beforeFx : function(){
22525         this.beforeAction();
22526         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22527     },
22528
22529     // private
22530     afterFx : function(){
22531         Roo.Layer.superclass.afterFx.apply(this, arguments);
22532         this.sync(this.isVisible());
22533     },
22534
22535     // private
22536     beforeAction : function(){
22537         if(!this.updating && this.shadow){
22538             this.shadow.hide();
22539         }
22540     },
22541
22542     // overridden Element method
22543     setLeft : function(left){
22544         this.storeLeftTop(left, this.getTop(true));
22545         supr.setLeft.apply(this, arguments);
22546         this.sync();
22547     },
22548
22549     setTop : function(top){
22550         this.storeLeftTop(this.getLeft(true), top);
22551         supr.setTop.apply(this, arguments);
22552         this.sync();
22553     },
22554
22555     setLeftTop : function(left, top){
22556         this.storeLeftTop(left, top);
22557         supr.setLeftTop.apply(this, arguments);
22558         this.sync();
22559     },
22560
22561     setXY : function(xy, a, d, c, e){
22562         this.fixDisplay();
22563         this.beforeAction();
22564         this.storeXY(xy);
22565         var cb = this.createCB(c);
22566         supr.setXY.call(this, xy, a, d, cb, e);
22567         if(!a){
22568             cb();
22569         }
22570     },
22571
22572     // private
22573     createCB : function(c){
22574         var el = this;
22575         return function(){
22576             el.constrainXY();
22577             el.sync(true);
22578             if(c){
22579                 c();
22580             }
22581         };
22582     },
22583
22584     // overridden Element method
22585     setX : function(x, a, d, c, e){
22586         this.setXY([x, this.getY()], a, d, c, e);
22587     },
22588
22589     // overridden Element method
22590     setY : function(y, a, d, c, e){
22591         this.setXY([this.getX(), y], a, d, c, e);
22592     },
22593
22594     // overridden Element method
22595     setSize : function(w, h, a, d, c, e){
22596         this.beforeAction();
22597         var cb = this.createCB(c);
22598         supr.setSize.call(this, w, h, a, d, cb, e);
22599         if(!a){
22600             cb();
22601         }
22602     },
22603
22604     // overridden Element method
22605     setWidth : function(w, a, d, c, e){
22606         this.beforeAction();
22607         var cb = this.createCB(c);
22608         supr.setWidth.call(this, w, a, d, cb, e);
22609         if(!a){
22610             cb();
22611         }
22612     },
22613
22614     // overridden Element method
22615     setHeight : function(h, a, d, c, e){
22616         this.beforeAction();
22617         var cb = this.createCB(c);
22618         supr.setHeight.call(this, h, a, d, cb, e);
22619         if(!a){
22620             cb();
22621         }
22622     },
22623
22624     // overridden Element method
22625     setBounds : function(x, y, w, h, a, d, c, e){
22626         this.beforeAction();
22627         var cb = this.createCB(c);
22628         if(!a){
22629             this.storeXY([x, y]);
22630             supr.setXY.call(this, [x, y]);
22631             supr.setSize.call(this, w, h, a, d, cb, e);
22632             cb();
22633         }else{
22634             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22635         }
22636         return this;
22637     },
22638     
22639     /**
22640      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22641      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22642      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22643      * @param {Number} zindex The new z-index to set
22644      * @return {this} The Layer
22645      */
22646     setZIndex : function(zindex){
22647         this.zindex = zindex;
22648         this.setStyle("z-index", zindex + 2);
22649         if(this.shadow){
22650             this.shadow.setZIndex(zindex + 1);
22651         }
22652         if(this.shim){
22653             this.shim.setStyle("z-index", zindex);
22654         }
22655     }
22656 });
22657 })();/*
22658  * Based on:
22659  * Ext JS Library 1.1.1
22660  * Copyright(c) 2006-2007, Ext JS, LLC.
22661  *
22662  * Originally Released Under LGPL - original licence link has changed is not relivant.
22663  *
22664  * Fork - LGPL
22665  * <script type="text/javascript">
22666  */
22667
22668
22669 /**
22670  * @class Roo.Shadow
22671  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22672  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22673  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22674  * @constructor
22675  * Create a new Shadow
22676  * @param {Object} config The config object
22677  */
22678 Roo.Shadow = function(config){
22679     Roo.apply(this, config);
22680     if(typeof this.mode != "string"){
22681         this.mode = this.defaultMode;
22682     }
22683     var o = this.offset, a = {h: 0};
22684     var rad = Math.floor(this.offset/2);
22685     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22686         case "drop":
22687             a.w = 0;
22688             a.l = a.t = o;
22689             a.t -= 1;
22690             if(Roo.isIE){
22691                 a.l -= this.offset + rad;
22692                 a.t -= this.offset + rad;
22693                 a.w -= rad;
22694                 a.h -= rad;
22695                 a.t += 1;
22696             }
22697         break;
22698         case "sides":
22699             a.w = (o*2);
22700             a.l = -o;
22701             a.t = o-1;
22702             if(Roo.isIE){
22703                 a.l -= (this.offset - rad);
22704                 a.t -= this.offset + rad;
22705                 a.l += 1;
22706                 a.w -= (this.offset - rad)*2;
22707                 a.w -= rad + 1;
22708                 a.h -= 1;
22709             }
22710         break;
22711         case "frame":
22712             a.w = a.h = (o*2);
22713             a.l = a.t = -o;
22714             a.t += 1;
22715             a.h -= 2;
22716             if(Roo.isIE){
22717                 a.l -= (this.offset - rad);
22718                 a.t -= (this.offset - rad);
22719                 a.l += 1;
22720                 a.w -= (this.offset + rad + 1);
22721                 a.h -= (this.offset + rad);
22722                 a.h += 1;
22723             }
22724         break;
22725     };
22726
22727     this.adjusts = a;
22728 };
22729
22730 Roo.Shadow.prototype = {
22731     /**
22732      * @cfg {String} mode
22733      * The shadow display mode.  Supports the following options:<br />
22734      * sides: Shadow displays on both sides and bottom only<br />
22735      * frame: Shadow displays equally on all four sides<br />
22736      * drop: Traditional bottom-right drop shadow (default)
22737      */
22738     /**
22739      * @cfg {String} offset
22740      * The number of pixels to offset the shadow from the element (defaults to 4)
22741      */
22742     offset: 4,
22743
22744     // private
22745     defaultMode: "drop",
22746
22747     /**
22748      * Displays the shadow under the target element
22749      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22750      */
22751     show : function(target){
22752         target = Roo.get(target);
22753         if(!this.el){
22754             this.el = Roo.Shadow.Pool.pull();
22755             if(this.el.dom.nextSibling != target.dom){
22756                 this.el.insertBefore(target);
22757             }
22758         }
22759         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22760         if(Roo.isIE){
22761             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22762         }
22763         this.realign(
22764             target.getLeft(true),
22765             target.getTop(true),
22766             target.getWidth(),
22767             target.getHeight()
22768         );
22769         this.el.dom.style.display = "block";
22770     },
22771
22772     /**
22773      * Returns true if the shadow is visible, else false
22774      */
22775     isVisible : function(){
22776         return this.el ? true : false;  
22777     },
22778
22779     /**
22780      * Direct alignment when values are already available. Show must be called at least once before
22781      * calling this method to ensure it is initialized.
22782      * @param {Number} left The target element left position
22783      * @param {Number} top The target element top position
22784      * @param {Number} width The target element width
22785      * @param {Number} height The target element height
22786      */
22787     realign : function(l, t, w, h){
22788         if(!this.el){
22789             return;
22790         }
22791         var a = this.adjusts, d = this.el.dom, s = d.style;
22792         var iea = 0;
22793         s.left = (l+a.l)+"px";
22794         s.top = (t+a.t)+"px";
22795         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22796  
22797         if(s.width != sws || s.height != shs){
22798             s.width = sws;
22799             s.height = shs;
22800             if(!Roo.isIE){
22801                 var cn = d.childNodes;
22802                 var sww = Math.max(0, (sw-12))+"px";
22803                 cn[0].childNodes[1].style.width = sww;
22804                 cn[1].childNodes[1].style.width = sww;
22805                 cn[2].childNodes[1].style.width = sww;
22806                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22807             }
22808         }
22809     },
22810
22811     /**
22812      * Hides this shadow
22813      */
22814     hide : function(){
22815         if(this.el){
22816             this.el.dom.style.display = "none";
22817             Roo.Shadow.Pool.push(this.el);
22818             delete this.el;
22819         }
22820     },
22821
22822     /**
22823      * Adjust the z-index of this shadow
22824      * @param {Number} zindex The new z-index
22825      */
22826     setZIndex : function(z){
22827         this.zIndex = z;
22828         if(this.el){
22829             this.el.setStyle("z-index", z);
22830         }
22831     }
22832 };
22833
22834 // Private utility class that manages the internal Shadow cache
22835 Roo.Shadow.Pool = function(){
22836     var p = [];
22837     var markup = Roo.isIE ?
22838                  '<div class="x-ie-shadow"></div>' :
22839                  '<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>';
22840     return {
22841         pull : function(){
22842             var sh = p.shift();
22843             if(!sh){
22844                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22845                 sh.autoBoxAdjust = false;
22846             }
22847             return sh;
22848         },
22849
22850         push : function(sh){
22851             p.push(sh);
22852         }
22853     };
22854 }();/*
22855  * Based on:
22856  * Ext JS Library 1.1.1
22857  * Copyright(c) 2006-2007, Ext JS, LLC.
22858  *
22859  * Originally Released Under LGPL - original licence link has changed is not relivant.
22860  *
22861  * Fork - LGPL
22862  * <script type="text/javascript">
22863  */
22864
22865 /**
22866  * @class Roo.BoxComponent
22867  * @extends Roo.Component
22868  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22869  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22870  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22871  * layout containers.
22872  * @constructor
22873  * @param {Roo.Element/String/Object} config The configuration options.
22874  */
22875 Roo.BoxComponent = function(config){
22876     Roo.Component.call(this, config);
22877     this.addEvents({
22878         /**
22879          * @event resize
22880          * Fires after the component is resized.
22881              * @param {Roo.Component} this
22882              * @param {Number} adjWidth The box-adjusted width that was set
22883              * @param {Number} adjHeight The box-adjusted height that was set
22884              * @param {Number} rawWidth The width that was originally specified
22885              * @param {Number} rawHeight The height that was originally specified
22886              */
22887         resize : true,
22888         /**
22889          * @event move
22890          * Fires after the component is moved.
22891              * @param {Roo.Component} this
22892              * @param {Number} x The new x position
22893              * @param {Number} y The new y position
22894              */
22895         move : true
22896     });
22897 };
22898
22899 Roo.extend(Roo.BoxComponent, Roo.Component, {
22900     // private, set in afterRender to signify that the component has been rendered
22901     boxReady : false,
22902     // private, used to defer height settings to subclasses
22903     deferHeight: false,
22904     /** @cfg {Number} width
22905      * width (optional) size of component
22906      */
22907      /** @cfg {Number} height
22908      * height (optional) size of component
22909      */
22910      
22911     /**
22912      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22913      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22914      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22915      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22916      * @return {Roo.BoxComponent} this
22917      */
22918     setSize : function(w, h){
22919         // support for standard size objects
22920         if(typeof w == 'object'){
22921             h = w.height;
22922             w = w.width;
22923         }
22924         // not rendered
22925         if(!this.boxReady){
22926             this.width = w;
22927             this.height = h;
22928             return this;
22929         }
22930
22931         // prevent recalcs when not needed
22932         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22933             return this;
22934         }
22935         this.lastSize = {width: w, height: h};
22936
22937         var adj = this.adjustSize(w, h);
22938         var aw = adj.width, ah = adj.height;
22939         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22940             var rz = this.getResizeEl();
22941             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22942                 rz.setSize(aw, ah);
22943             }else if(!this.deferHeight && ah !== undefined){
22944                 rz.setHeight(ah);
22945             }else if(aw !== undefined){
22946                 rz.setWidth(aw);
22947             }
22948             this.onResize(aw, ah, w, h);
22949             this.fireEvent('resize', this, aw, ah, w, h);
22950         }
22951         return this;
22952     },
22953
22954     /**
22955      * Gets the current size of the component's underlying element.
22956      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22957      */
22958     getSize : function(){
22959         return this.el.getSize();
22960     },
22961
22962     /**
22963      * Gets the current XY position of the component's underlying element.
22964      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22965      * @return {Array} The XY position of the element (e.g., [100, 200])
22966      */
22967     getPosition : function(local){
22968         if(local === true){
22969             return [this.el.getLeft(true), this.el.getTop(true)];
22970         }
22971         return this.xy || this.el.getXY();
22972     },
22973
22974     /**
22975      * Gets the current box measurements of the component's underlying element.
22976      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22977      * @returns {Object} box An object in the format {x, y, width, height}
22978      */
22979     getBox : function(local){
22980         var s = this.el.getSize();
22981         if(local){
22982             s.x = this.el.getLeft(true);
22983             s.y = this.el.getTop(true);
22984         }else{
22985             var xy = this.xy || this.el.getXY();
22986             s.x = xy[0];
22987             s.y = xy[1];
22988         }
22989         return s;
22990     },
22991
22992     /**
22993      * Sets the current box measurements of the component's underlying element.
22994      * @param {Object} box An object in the format {x, y, width, height}
22995      * @returns {Roo.BoxComponent} this
22996      */
22997     updateBox : function(box){
22998         this.setSize(box.width, box.height);
22999         this.setPagePosition(box.x, box.y);
23000         return this;
23001     },
23002
23003     // protected
23004     getResizeEl : function(){
23005         return this.resizeEl || this.el;
23006     },
23007
23008     // protected
23009     getPositionEl : function(){
23010         return this.positionEl || this.el;
23011     },
23012
23013     /**
23014      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
23015      * This method fires the move event.
23016      * @param {Number} left The new left
23017      * @param {Number} top The new top
23018      * @returns {Roo.BoxComponent} this
23019      */
23020     setPosition : function(x, y){
23021         this.x = x;
23022         this.y = y;
23023         if(!this.boxReady){
23024             return this;
23025         }
23026         var adj = this.adjustPosition(x, y);
23027         var ax = adj.x, ay = adj.y;
23028
23029         var el = this.getPositionEl();
23030         if(ax !== undefined || ay !== undefined){
23031             if(ax !== undefined && ay !== undefined){
23032                 el.setLeftTop(ax, ay);
23033             }else if(ax !== undefined){
23034                 el.setLeft(ax);
23035             }else if(ay !== undefined){
23036                 el.setTop(ay);
23037             }
23038             this.onPosition(ax, ay);
23039             this.fireEvent('move', this, ax, ay);
23040         }
23041         return this;
23042     },
23043
23044     /**
23045      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
23046      * This method fires the move event.
23047      * @param {Number} x The new x position
23048      * @param {Number} y The new y position
23049      * @returns {Roo.BoxComponent} this
23050      */
23051     setPagePosition : function(x, y){
23052         this.pageX = x;
23053         this.pageY = y;
23054         if(!this.boxReady){
23055             return;
23056         }
23057         if(x === undefined || y === undefined){ // cannot translate undefined points
23058             return;
23059         }
23060         var p = this.el.translatePoints(x, y);
23061         this.setPosition(p.left, p.top);
23062         return this;
23063     },
23064
23065     // private
23066     onRender : function(ct, position){
23067         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
23068         if(this.resizeEl){
23069             this.resizeEl = Roo.get(this.resizeEl);
23070         }
23071         if(this.positionEl){
23072             this.positionEl = Roo.get(this.positionEl);
23073         }
23074     },
23075
23076     // private
23077     afterRender : function(){
23078         Roo.BoxComponent.superclass.afterRender.call(this);
23079         this.boxReady = true;
23080         this.setSize(this.width, this.height);
23081         if(this.x || this.y){
23082             this.setPosition(this.x, this.y);
23083         }
23084         if(this.pageX || this.pageY){
23085             this.setPagePosition(this.pageX, this.pageY);
23086         }
23087     },
23088
23089     /**
23090      * Force the component's size to recalculate based on the underlying element's current height and width.
23091      * @returns {Roo.BoxComponent} this
23092      */
23093     syncSize : function(){
23094         delete this.lastSize;
23095         this.setSize(this.el.getWidth(), this.el.getHeight());
23096         return this;
23097     },
23098
23099     /**
23100      * Called after the component is resized, this method is empty by default but can be implemented by any
23101      * subclass that needs to perform custom logic after a resize occurs.
23102      * @param {Number} adjWidth The box-adjusted width that was set
23103      * @param {Number} adjHeight The box-adjusted height that was set
23104      * @param {Number} rawWidth The width that was originally specified
23105      * @param {Number} rawHeight The height that was originally specified
23106      */
23107     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
23108
23109     },
23110
23111     /**
23112      * Called after the component is moved, this method is empty by default but can be implemented by any
23113      * subclass that needs to perform custom logic after a move occurs.
23114      * @param {Number} x The new x position
23115      * @param {Number} y The new y position
23116      */
23117     onPosition : function(x, y){
23118
23119     },
23120
23121     // private
23122     adjustSize : function(w, h){
23123         if(this.autoWidth){
23124             w = 'auto';
23125         }
23126         if(this.autoHeight){
23127             h = 'auto';
23128         }
23129         return {width : w, height: h};
23130     },
23131
23132     // private
23133     adjustPosition : function(x, y){
23134         return {x : x, y: y};
23135     }
23136 });/*
23137  * Based on:
23138  * Ext JS Library 1.1.1
23139  * Copyright(c) 2006-2007, Ext JS, LLC.
23140  *
23141  * Originally Released Under LGPL - original licence link has changed is not relivant.
23142  *
23143  * Fork - LGPL
23144  * <script type="text/javascript">
23145  */
23146
23147
23148 /**
23149  * @class Roo.SplitBar
23150  * @extends Roo.util.Observable
23151  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23152  * <br><br>
23153  * Usage:
23154  * <pre><code>
23155 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23156                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23157 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23158 split.minSize = 100;
23159 split.maxSize = 600;
23160 split.animate = true;
23161 split.on('moved', splitterMoved);
23162 </code></pre>
23163  * @constructor
23164  * Create a new SplitBar
23165  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23166  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23167  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23168  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23169                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23170                         position of the SplitBar).
23171  */
23172 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23173     
23174     /** @private */
23175     this.el = Roo.get(dragElement, true);
23176     this.el.dom.unselectable = "on";
23177     /** @private */
23178     this.resizingEl = Roo.get(resizingElement, true);
23179
23180     /**
23181      * @private
23182      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23183      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23184      * @type Number
23185      */
23186     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23187     
23188     /**
23189      * The minimum size of the resizing element. (Defaults to 0)
23190      * @type Number
23191      */
23192     this.minSize = 0;
23193     
23194     /**
23195      * The maximum size of the resizing element. (Defaults to 2000)
23196      * @type Number
23197      */
23198     this.maxSize = 2000;
23199     
23200     /**
23201      * Whether to animate the transition to the new size
23202      * @type Boolean
23203      */
23204     this.animate = false;
23205     
23206     /**
23207      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23208      * @type Boolean
23209      */
23210     this.useShim = false;
23211     
23212     /** @private */
23213     this.shim = null;
23214     
23215     if(!existingProxy){
23216         /** @private */
23217         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23218     }else{
23219         this.proxy = Roo.get(existingProxy).dom;
23220     }
23221     /** @private */
23222     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23223     
23224     /** @private */
23225     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23226     
23227     /** @private */
23228     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23229     
23230     /** @private */
23231     this.dragSpecs = {};
23232     
23233     /**
23234      * @private The adapter to use to positon and resize elements
23235      */
23236     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23237     this.adapter.init(this);
23238     
23239     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23240         /** @private */
23241         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23242         this.el.addClass("x-splitbar-h");
23243     }else{
23244         /** @private */
23245         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23246         this.el.addClass("x-splitbar-v");
23247     }
23248     
23249     this.addEvents({
23250         /**
23251          * @event resize
23252          * Fires when the splitter is moved (alias for {@link #event-moved})
23253          * @param {Roo.SplitBar} this
23254          * @param {Number} newSize the new width or height
23255          */
23256         "resize" : true,
23257         /**
23258          * @event moved
23259          * Fires when the splitter is moved
23260          * @param {Roo.SplitBar} this
23261          * @param {Number} newSize the new width or height
23262          */
23263         "moved" : true,
23264         /**
23265          * @event beforeresize
23266          * Fires before the splitter is dragged
23267          * @param {Roo.SplitBar} this
23268          */
23269         "beforeresize" : true,
23270
23271         "beforeapply" : true
23272     });
23273
23274     Roo.util.Observable.call(this);
23275 };
23276
23277 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23278     onStartProxyDrag : function(x, y){
23279         this.fireEvent("beforeresize", this);
23280         if(!this.overlay){
23281             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23282             o.unselectable();
23283             o.enableDisplayMode("block");
23284             // all splitbars share the same overlay
23285             Roo.SplitBar.prototype.overlay = o;
23286         }
23287         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23288         this.overlay.show();
23289         Roo.get(this.proxy).setDisplayed("block");
23290         var size = this.adapter.getElementSize(this);
23291         this.activeMinSize = this.getMinimumSize();;
23292         this.activeMaxSize = this.getMaximumSize();;
23293         var c1 = size - this.activeMinSize;
23294         var c2 = Math.max(this.activeMaxSize - size, 0);
23295         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23296             this.dd.resetConstraints();
23297             this.dd.setXConstraint(
23298                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23299                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23300             );
23301             this.dd.setYConstraint(0, 0);
23302         }else{
23303             this.dd.resetConstraints();
23304             this.dd.setXConstraint(0, 0);
23305             this.dd.setYConstraint(
23306                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23307                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23308             );
23309          }
23310         this.dragSpecs.startSize = size;
23311         this.dragSpecs.startPoint = [x, y];
23312         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23313     },
23314     
23315     /** 
23316      * @private Called after the drag operation by the DDProxy
23317      */
23318     onEndProxyDrag : function(e){
23319         Roo.get(this.proxy).setDisplayed(false);
23320         var endPoint = Roo.lib.Event.getXY(e);
23321         if(this.overlay){
23322             this.overlay.hide();
23323         }
23324         var newSize;
23325         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23326             newSize = this.dragSpecs.startSize + 
23327                 (this.placement == Roo.SplitBar.LEFT ?
23328                     endPoint[0] - this.dragSpecs.startPoint[0] :
23329                     this.dragSpecs.startPoint[0] - endPoint[0]
23330                 );
23331         }else{
23332             newSize = this.dragSpecs.startSize + 
23333                 (this.placement == Roo.SplitBar.TOP ?
23334                     endPoint[1] - this.dragSpecs.startPoint[1] :
23335                     this.dragSpecs.startPoint[1] - endPoint[1]
23336                 );
23337         }
23338         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23339         if(newSize != this.dragSpecs.startSize){
23340             if(this.fireEvent('beforeapply', this, newSize) !== false){
23341                 this.adapter.setElementSize(this, newSize);
23342                 this.fireEvent("moved", this, newSize);
23343                 this.fireEvent("resize", this, newSize);
23344             }
23345         }
23346     },
23347     
23348     /**
23349      * Get the adapter this SplitBar uses
23350      * @return The adapter object
23351      */
23352     getAdapter : function(){
23353         return this.adapter;
23354     },
23355     
23356     /**
23357      * Set the adapter this SplitBar uses
23358      * @param {Object} adapter A SplitBar adapter object
23359      */
23360     setAdapter : function(adapter){
23361         this.adapter = adapter;
23362         this.adapter.init(this);
23363     },
23364     
23365     /**
23366      * Gets the minimum size for the resizing element
23367      * @return {Number} The minimum size
23368      */
23369     getMinimumSize : function(){
23370         return this.minSize;
23371     },
23372     
23373     /**
23374      * Sets the minimum size for the resizing element
23375      * @param {Number} minSize The minimum size
23376      */
23377     setMinimumSize : function(minSize){
23378         this.minSize = minSize;
23379     },
23380     
23381     /**
23382      * Gets the maximum size for the resizing element
23383      * @return {Number} The maximum size
23384      */
23385     getMaximumSize : function(){
23386         return this.maxSize;
23387     },
23388     
23389     /**
23390      * Sets the maximum size for the resizing element
23391      * @param {Number} maxSize The maximum size
23392      */
23393     setMaximumSize : function(maxSize){
23394         this.maxSize = maxSize;
23395     },
23396     
23397     /**
23398      * Sets the initialize size for the resizing element
23399      * @param {Number} size The initial size
23400      */
23401     setCurrentSize : function(size){
23402         var oldAnimate = this.animate;
23403         this.animate = false;
23404         this.adapter.setElementSize(this, size);
23405         this.animate = oldAnimate;
23406     },
23407     
23408     /**
23409      * Destroy this splitbar. 
23410      * @param {Boolean} removeEl True to remove the element
23411      */
23412     destroy : function(removeEl){
23413         if(this.shim){
23414             this.shim.remove();
23415         }
23416         this.dd.unreg();
23417         this.proxy.parentNode.removeChild(this.proxy);
23418         if(removeEl){
23419             this.el.remove();
23420         }
23421     }
23422 });
23423
23424 /**
23425  * @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.
23426  */
23427 Roo.SplitBar.createProxy = function(dir){
23428     var proxy = new Roo.Element(document.createElement("div"));
23429     proxy.unselectable();
23430     var cls = 'x-splitbar-proxy';
23431     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23432     document.body.appendChild(proxy.dom);
23433     return proxy.dom;
23434 };
23435
23436 /** 
23437  * @class Roo.SplitBar.BasicLayoutAdapter
23438  * Default Adapter. It assumes the splitter and resizing element are not positioned
23439  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23440  */
23441 Roo.SplitBar.BasicLayoutAdapter = function(){
23442 };
23443
23444 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23445     // do nothing for now
23446     init : function(s){
23447     
23448     },
23449     /**
23450      * Called before drag operations to get the current size of the resizing element. 
23451      * @param {Roo.SplitBar} s The SplitBar using this adapter
23452      */
23453      getElementSize : function(s){
23454         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23455             return s.resizingEl.getWidth();
23456         }else{
23457             return s.resizingEl.getHeight();
23458         }
23459     },
23460     
23461     /**
23462      * Called after drag operations to set the size of the resizing element.
23463      * @param {Roo.SplitBar} s The SplitBar using this adapter
23464      * @param {Number} newSize The new size to set
23465      * @param {Function} onComplete A function to be invoked when resizing is complete
23466      */
23467     setElementSize : function(s, newSize, onComplete){
23468         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23469             if(!s.animate){
23470                 s.resizingEl.setWidth(newSize);
23471                 if(onComplete){
23472                     onComplete(s, newSize);
23473                 }
23474             }else{
23475                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23476             }
23477         }else{
23478             
23479             if(!s.animate){
23480                 s.resizingEl.setHeight(newSize);
23481                 if(onComplete){
23482                     onComplete(s, newSize);
23483                 }
23484             }else{
23485                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23486             }
23487         }
23488     }
23489 };
23490
23491 /** 
23492  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23493  * @extends Roo.SplitBar.BasicLayoutAdapter
23494  * Adapter that  moves the splitter element to align with the resized sizing element. 
23495  * Used with an absolute positioned SplitBar.
23496  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23497  * document.body, make sure you assign an id to the body element.
23498  */
23499 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23500     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23501     this.container = Roo.get(container);
23502 };
23503
23504 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23505     init : function(s){
23506         this.basic.init(s);
23507     },
23508     
23509     getElementSize : function(s){
23510         return this.basic.getElementSize(s);
23511     },
23512     
23513     setElementSize : function(s, newSize, onComplete){
23514         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23515     },
23516     
23517     moveSplitter : function(s){
23518         var yes = Roo.SplitBar;
23519         switch(s.placement){
23520             case yes.LEFT:
23521                 s.el.setX(s.resizingEl.getRight());
23522                 break;
23523             case yes.RIGHT:
23524                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23525                 break;
23526             case yes.TOP:
23527                 s.el.setY(s.resizingEl.getBottom());
23528                 break;
23529             case yes.BOTTOM:
23530                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23531                 break;
23532         }
23533     }
23534 };
23535
23536 /**
23537  * Orientation constant - Create a vertical SplitBar
23538  * @static
23539  * @type Number
23540  */
23541 Roo.SplitBar.VERTICAL = 1;
23542
23543 /**
23544  * Orientation constant - Create a horizontal SplitBar
23545  * @static
23546  * @type Number
23547  */
23548 Roo.SplitBar.HORIZONTAL = 2;
23549
23550 /**
23551  * Placement constant - The resizing element is to the left of the splitter element
23552  * @static
23553  * @type Number
23554  */
23555 Roo.SplitBar.LEFT = 1;
23556
23557 /**
23558  * Placement constant - The resizing element is to the right of the splitter element
23559  * @static
23560  * @type Number
23561  */
23562 Roo.SplitBar.RIGHT = 2;
23563
23564 /**
23565  * Placement constant - The resizing element is positioned above the splitter element
23566  * @static
23567  * @type Number
23568  */
23569 Roo.SplitBar.TOP = 3;
23570
23571 /**
23572  * Placement constant - The resizing element is positioned under splitter element
23573  * @static
23574  * @type Number
23575  */
23576 Roo.SplitBar.BOTTOM = 4;
23577 /*
23578  * Based on:
23579  * Ext JS Library 1.1.1
23580  * Copyright(c) 2006-2007, Ext JS, LLC.
23581  *
23582  * Originally Released Under LGPL - original licence link has changed is not relivant.
23583  *
23584  * Fork - LGPL
23585  * <script type="text/javascript">
23586  */
23587
23588 /**
23589  * @class Roo.View
23590  * @extends Roo.util.Observable
23591  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23592  * This class also supports single and multi selection modes. <br>
23593  * Create a data model bound view:
23594  <pre><code>
23595  var store = new Roo.data.Store(...);
23596
23597  var view = new Roo.View({
23598     el : "my-element",
23599     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23600  
23601     singleSelect: true,
23602     selectedClass: "ydataview-selected",
23603     store: store
23604  });
23605
23606  // listen for node click?
23607  view.on("click", function(vw, index, node, e){
23608  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23609  });
23610
23611  // load XML data
23612  dataModel.load("foobar.xml");
23613  </code></pre>
23614  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23615  * <br><br>
23616  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23617  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23618  * 
23619  * Note: old style constructor is still suported (container, template, config)
23620  * 
23621  * @constructor
23622  * Create a new View
23623  * @param {Object} config The config object
23624  * 
23625  */
23626 Roo.View = function(config, depreciated_tpl, depreciated_config){
23627     
23628     if (typeof(depreciated_tpl) == 'undefined') {
23629         // new way.. - universal constructor.
23630         Roo.apply(this, config);
23631         this.el  = Roo.get(this.el);
23632     } else {
23633         // old format..
23634         this.el  = Roo.get(config);
23635         this.tpl = depreciated_tpl;
23636         Roo.apply(this, depreciated_config);
23637     }
23638      
23639     
23640     if(typeof(this.tpl) == "string"){
23641         this.tpl = new Roo.Template(this.tpl);
23642     } else {
23643         // support xtype ctors..
23644         this.tpl = new Roo.factory(this.tpl, Roo);
23645     }
23646     
23647     
23648     this.tpl.compile();
23649    
23650
23651      
23652     /** @private */
23653     this.addEvents({
23654         /**
23655          * @event beforeclick
23656          * Fires before a click is processed. Returns false to cancel the default action.
23657          * @param {Roo.View} this
23658          * @param {Number} index The index of the target node
23659          * @param {HTMLElement} node The target node
23660          * @param {Roo.EventObject} e The raw event object
23661          */
23662             "beforeclick" : true,
23663         /**
23664          * @event click
23665          * Fires when a template node is clicked.
23666          * @param {Roo.View} this
23667          * @param {Number} index The index of the target node
23668          * @param {HTMLElement} node The target node
23669          * @param {Roo.EventObject} e The raw event object
23670          */
23671             "click" : true,
23672         /**
23673          * @event dblclick
23674          * Fires when a template node is double clicked.
23675          * @param {Roo.View} this
23676          * @param {Number} index The index of the target node
23677          * @param {HTMLElement} node The target node
23678          * @param {Roo.EventObject} e The raw event object
23679          */
23680             "dblclick" : true,
23681         /**
23682          * @event contextmenu
23683          * Fires when a template node is right clicked.
23684          * @param {Roo.View} this
23685          * @param {Number} index The index of the target node
23686          * @param {HTMLElement} node The target node
23687          * @param {Roo.EventObject} e The raw event object
23688          */
23689             "contextmenu" : true,
23690         /**
23691          * @event selectionchange
23692          * Fires when the selected nodes change.
23693          * @param {Roo.View} this
23694          * @param {Array} selections Array of the selected nodes
23695          */
23696             "selectionchange" : true,
23697     
23698         /**
23699          * @event beforeselect
23700          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23701          * @param {Roo.View} this
23702          * @param {HTMLElement} node The node to be selected
23703          * @param {Array} selections Array of currently selected nodes
23704          */
23705             "beforeselect" : true,
23706         /**
23707          * @event preparedata
23708          * Fires on every row to render, to allow you to change the data.
23709          * @param {Roo.View} this
23710          * @param {Object} data to be rendered (change this)
23711          */
23712           "preparedata" : true
23713         });
23714
23715     this.el.on({
23716         "click": this.onClick,
23717         "dblclick": this.onDblClick,
23718         "contextmenu": this.onContextMenu,
23719         scope:this
23720     });
23721
23722     this.selections = [];
23723     this.nodes = [];
23724     this.cmp = new Roo.CompositeElementLite([]);
23725     if(this.store){
23726         this.store = Roo.factory(this.store, Roo.data);
23727         this.setStore(this.store, true);
23728     }
23729     Roo.View.superclass.constructor.call(this);
23730 };
23731
23732 Roo.extend(Roo.View, Roo.util.Observable, {
23733     
23734      /**
23735      * @cfg {Roo.data.Store} store Data store to load data from.
23736      */
23737     store : false,
23738     
23739     /**
23740      * @cfg {String|Roo.Element} el The container element.
23741      */
23742     el : '',
23743     
23744     /**
23745      * @cfg {String|Roo.Template} tpl The template used by this View 
23746      */
23747     tpl : false,
23748     
23749     /**
23750      * @cfg {String} selectedClass The css class to add to selected nodes
23751      */
23752     selectedClass : "x-view-selected",
23753      /**
23754      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23755      */
23756     emptyText : "",
23757     /**
23758      * @cfg {Boolean} multiSelect Allow multiple selection
23759      */
23760     multiSelect : false,
23761     /**
23762      * @cfg {Boolean} singleSelect Allow single selection
23763      */
23764     singleSelect:  false,
23765     
23766     /**
23767      * @cfg {Boolean} toggleSelect - selecting 
23768      */
23769     toggleSelect : false,
23770     
23771     /**
23772      * Returns the element this view is bound to.
23773      * @return {Roo.Element}
23774      */
23775     getEl : function(){
23776         return this.el;
23777     },
23778
23779     /**
23780      * Refreshes the view.
23781      */
23782     refresh : function(){
23783         var t = this.tpl;
23784         this.clearSelections();
23785         this.el.update("");
23786         var html = [];
23787         var records = this.store.getRange();
23788         if(records.length < 1){
23789             this.el.update(this.emptyText);
23790             return;
23791         }
23792         for(var i = 0, len = records.length; i < len; i++){
23793             var data = this.prepareData(records[i].data, i, records[i]);
23794             this.fireEvent("preparedata", this, data, i, records[i]);
23795             html[html.length] = t.apply(data);
23796         }
23797         this.el.update(html.join(""));
23798         this.nodes = this.el.dom.childNodes;
23799         this.updateIndexes(0);
23800     },
23801
23802     /**
23803      * Function to override to reformat the data that is sent to
23804      * the template for each node.
23805      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23806      * a JSON object for an UpdateManager bound view).
23807      */
23808     prepareData : function(data){
23809         return data;
23810     },
23811
23812     onUpdate : function(ds, record){
23813         this.clearSelections();
23814         var index = this.store.indexOf(record);
23815         var n = this.nodes[index];
23816         this.tpl.insertBefore(n, this.prepareData(record.data));
23817         n.parentNode.removeChild(n);
23818         this.updateIndexes(index, index);
23819     },
23820
23821     onAdd : function(ds, records, index){
23822         this.clearSelections();
23823         if(this.nodes.length == 0){
23824             this.refresh();
23825             return;
23826         }
23827         var n = this.nodes[index];
23828         for(var i = 0, len = records.length; i < len; i++){
23829             var d = this.prepareData(records[i].data);
23830             if(n){
23831                 this.tpl.insertBefore(n, d);
23832             }else{
23833                 this.tpl.append(this.el, d);
23834             }
23835         }
23836         this.updateIndexes(index);
23837     },
23838
23839     onRemove : function(ds, record, index){
23840         this.clearSelections();
23841         this.el.dom.removeChild(this.nodes[index]);
23842         this.updateIndexes(index);
23843     },
23844
23845     /**
23846      * Refresh an individual node.
23847      * @param {Number} index
23848      */
23849     refreshNode : function(index){
23850         this.onUpdate(this.store, this.store.getAt(index));
23851     },
23852
23853     updateIndexes : function(startIndex, endIndex){
23854         var ns = this.nodes;
23855         startIndex = startIndex || 0;
23856         endIndex = endIndex || ns.length - 1;
23857         for(var i = startIndex; i <= endIndex; i++){
23858             ns[i].nodeIndex = i;
23859         }
23860     },
23861
23862     /**
23863      * Changes the data store this view uses and refresh the view.
23864      * @param {Store} store
23865      */
23866     setStore : function(store, initial){
23867         if(!initial && this.store){
23868             this.store.un("datachanged", this.refresh);
23869             this.store.un("add", this.onAdd);
23870             this.store.un("remove", this.onRemove);
23871             this.store.un("update", this.onUpdate);
23872             this.store.un("clear", this.refresh);
23873         }
23874         if(store){
23875           
23876             store.on("datachanged", this.refresh, this);
23877             store.on("add", this.onAdd, this);
23878             store.on("remove", this.onRemove, this);
23879             store.on("update", this.onUpdate, this);
23880             store.on("clear", this.refresh, this);
23881         }
23882         
23883         if(store){
23884             this.refresh();
23885         }
23886     },
23887
23888     /**
23889      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23890      * @param {HTMLElement} node
23891      * @return {HTMLElement} The template node
23892      */
23893     findItemFromChild : function(node){
23894         var el = this.el.dom;
23895         if(!node || node.parentNode == el){
23896                     return node;
23897             }
23898             var p = node.parentNode;
23899             while(p && p != el){
23900             if(p.parentNode == el){
23901                 return p;
23902             }
23903             p = p.parentNode;
23904         }
23905             return null;
23906     },
23907
23908     /** @ignore */
23909     onClick : function(e){
23910         var item = this.findItemFromChild(e.getTarget());
23911         if(item){
23912             var index = this.indexOf(item);
23913             if(this.onItemClick(item, index, e) !== false){
23914                 this.fireEvent("click", this, index, item, e);
23915             }
23916         }else{
23917             this.clearSelections();
23918         }
23919     },
23920
23921     /** @ignore */
23922     onContextMenu : function(e){
23923         var item = this.findItemFromChild(e.getTarget());
23924         if(item){
23925             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23926         }
23927     },
23928
23929     /** @ignore */
23930     onDblClick : function(e){
23931         var item = this.findItemFromChild(e.getTarget());
23932         if(item){
23933             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23934         }
23935     },
23936
23937     onItemClick : function(item, index, e)
23938     {
23939         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23940             return false;
23941         }
23942         if (this.toggleSelect) {
23943             var m = this.isSelected(item) ? 'unselect' : 'select';
23944             Roo.log(m);
23945             var _t = this;
23946             _t[m](item, true, false);
23947             return true;
23948         }
23949         if(this.multiSelect || this.singleSelect){
23950             if(this.multiSelect && e.shiftKey && this.lastSelection){
23951                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23952             }else{
23953                 this.select(item, this.multiSelect && e.ctrlKey);
23954                 this.lastSelection = item;
23955             }
23956             e.preventDefault();
23957         }
23958         return true;
23959     },
23960
23961     /**
23962      * Get the number of selected nodes.
23963      * @return {Number}
23964      */
23965     getSelectionCount : function(){
23966         return this.selections.length;
23967     },
23968
23969     /**
23970      * Get the currently selected nodes.
23971      * @return {Array} An array of HTMLElements
23972      */
23973     getSelectedNodes : function(){
23974         return this.selections;
23975     },
23976
23977     /**
23978      * Get the indexes of the selected nodes.
23979      * @return {Array}
23980      */
23981     getSelectedIndexes : function(){
23982         var indexes = [], s = this.selections;
23983         for(var i = 0, len = s.length; i < len; i++){
23984             indexes.push(s[i].nodeIndex);
23985         }
23986         return indexes;
23987     },
23988
23989     /**
23990      * Clear all selections
23991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23992      */
23993     clearSelections : function(suppressEvent){
23994         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23995             this.cmp.elements = this.selections;
23996             this.cmp.removeClass(this.selectedClass);
23997             this.selections = [];
23998             if(!suppressEvent){
23999                 this.fireEvent("selectionchange", this, this.selections);
24000             }
24001         }
24002     },
24003
24004     /**
24005      * Returns true if the passed node is selected
24006      * @param {HTMLElement/Number} node The node or node index
24007      * @return {Boolean}
24008      */
24009     isSelected : function(node){
24010         var s = this.selections;
24011         if(s.length < 1){
24012             return false;
24013         }
24014         node = this.getNode(node);
24015         return s.indexOf(node) !== -1;
24016     },
24017
24018     /**
24019      * Selects nodes.
24020      * @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
24021      * @param {Boolean} keepExisting (optional) true to keep existing selections
24022      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24023      */
24024     select : function(nodeInfo, keepExisting, suppressEvent){
24025         if(nodeInfo instanceof Array){
24026             if(!keepExisting){
24027                 this.clearSelections(true);
24028             }
24029             for(var i = 0, len = nodeInfo.length; i < len; i++){
24030                 this.select(nodeInfo[i], true, true);
24031             }
24032             return;
24033         } 
24034         var node = this.getNode(nodeInfo);
24035         if(!node || this.isSelected(node)){
24036             return; // already selected.
24037         }
24038         if(!keepExisting){
24039             this.clearSelections(true);
24040         }
24041         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
24042             Roo.fly(node).addClass(this.selectedClass);
24043             this.selections.push(node);
24044             if(!suppressEvent){
24045                 this.fireEvent("selectionchange", this, this.selections);
24046             }
24047         }
24048         
24049         
24050     },
24051       /**
24052      * Unselects nodes.
24053      * @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
24054      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
24055      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
24056      */
24057     unselect : function(nodeInfo, keepExisting, suppressEvent)
24058     {
24059         if(nodeInfo instanceof Array){
24060             Roo.each(this.selections, function(s) {
24061                 this.unselect(s, nodeInfo);
24062             }, this);
24063             return;
24064         }
24065         var node = this.getNode(nodeInfo);
24066         if(!node || !this.isSelected(node)){
24067             Roo.log("not selected");
24068             return; // not selected.
24069         }
24070         // fireevent???
24071         var ns = [];
24072         Roo.each(this.selections, function(s) {
24073             if (s == node ) {
24074                 Roo.fly(node).removeClass(this.selectedClass);
24075
24076                 return;
24077             }
24078             ns.push(s);
24079         },this);
24080         
24081         this.selections= ns;
24082         this.fireEvent("selectionchange", this, this.selections);
24083     },
24084
24085     /**
24086      * Gets a template node.
24087      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24088      * @return {HTMLElement} The node or null if it wasn't found
24089      */
24090     getNode : function(nodeInfo){
24091         if(typeof nodeInfo == "string"){
24092             return document.getElementById(nodeInfo);
24093         }else if(typeof nodeInfo == "number"){
24094             return this.nodes[nodeInfo];
24095         }
24096         return nodeInfo;
24097     },
24098
24099     /**
24100      * Gets a range template nodes.
24101      * @param {Number} startIndex
24102      * @param {Number} endIndex
24103      * @return {Array} An array of nodes
24104      */
24105     getNodes : function(start, end){
24106         var ns = this.nodes;
24107         start = start || 0;
24108         end = typeof end == "undefined" ? ns.length - 1 : end;
24109         var nodes = [];
24110         if(start <= end){
24111             for(var i = start; i <= end; i++){
24112                 nodes.push(ns[i]);
24113             }
24114         } else{
24115             for(var i = start; i >= end; i--){
24116                 nodes.push(ns[i]);
24117             }
24118         }
24119         return nodes;
24120     },
24121
24122     /**
24123      * Finds the index of the passed node
24124      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
24125      * @return {Number} The index of the node or -1
24126      */
24127     indexOf : function(node){
24128         node = this.getNode(node);
24129         if(typeof node.nodeIndex == "number"){
24130             return node.nodeIndex;
24131         }
24132         var ns = this.nodes;
24133         for(var i = 0, len = ns.length; i < len; i++){
24134             if(ns[i] == node){
24135                 return i;
24136             }
24137         }
24138         return -1;
24139     }
24140 });
24141 /*
24142  * Based on:
24143  * Ext JS Library 1.1.1
24144  * Copyright(c) 2006-2007, Ext JS, LLC.
24145  *
24146  * Originally Released Under LGPL - original licence link has changed is not relivant.
24147  *
24148  * Fork - LGPL
24149  * <script type="text/javascript">
24150  */
24151
24152 /**
24153  * @class Roo.JsonView
24154  * @extends Roo.View
24155  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
24156 <pre><code>
24157 var view = new Roo.JsonView({
24158     container: "my-element",
24159     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
24160     multiSelect: true, 
24161     jsonRoot: "data" 
24162 });
24163
24164 // listen for node click?
24165 view.on("click", function(vw, index, node, e){
24166     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24167 });
24168
24169 // direct load of JSON data
24170 view.load("foobar.php");
24171
24172 // Example from my blog list
24173 var tpl = new Roo.Template(
24174     '&lt;div class="entry"&gt;' +
24175     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
24176     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
24177     "&lt;/div&gt;&lt;hr /&gt;"
24178 );
24179
24180 var moreView = new Roo.JsonView({
24181     container :  "entry-list", 
24182     template : tpl,
24183     jsonRoot: "posts"
24184 });
24185 moreView.on("beforerender", this.sortEntries, this);
24186 moreView.load({
24187     url: "/blog/get-posts.php",
24188     params: "allposts=true",
24189     text: "Loading Blog Entries..."
24190 });
24191 </code></pre>
24192
24193 * Note: old code is supported with arguments : (container, template, config)
24194
24195
24196  * @constructor
24197  * Create a new JsonView
24198  * 
24199  * @param {Object} config The config object
24200  * 
24201  */
24202 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24203     
24204     
24205     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24206
24207     var um = this.el.getUpdateManager();
24208     um.setRenderer(this);
24209     um.on("update", this.onLoad, this);
24210     um.on("failure", this.onLoadException, this);
24211
24212     /**
24213      * @event beforerender
24214      * Fires before rendering of the downloaded JSON data.
24215      * @param {Roo.JsonView} this
24216      * @param {Object} data The JSON data loaded
24217      */
24218     /**
24219      * @event load
24220      * Fires when data is loaded.
24221      * @param {Roo.JsonView} this
24222      * @param {Object} data The JSON data loaded
24223      * @param {Object} response The raw Connect response object
24224      */
24225     /**
24226      * @event loadexception
24227      * Fires when loading fails.
24228      * @param {Roo.JsonView} this
24229      * @param {Object} response The raw Connect response object
24230      */
24231     this.addEvents({
24232         'beforerender' : true,
24233         'load' : true,
24234         'loadexception' : true
24235     });
24236 };
24237 Roo.extend(Roo.JsonView, Roo.View, {
24238     /**
24239      * @type {String} The root property in the loaded JSON object that contains the data
24240      */
24241     jsonRoot : "",
24242
24243     /**
24244      * Refreshes the view.
24245      */
24246     refresh : function(){
24247         this.clearSelections();
24248         this.el.update("");
24249         var html = [];
24250         var o = this.jsonData;
24251         if(o && o.length > 0){
24252             for(var i = 0, len = o.length; i < len; i++){
24253                 var data = this.prepareData(o[i], i, o);
24254                 html[html.length] = this.tpl.apply(data);
24255             }
24256         }else{
24257             html.push(this.emptyText);
24258         }
24259         this.el.update(html.join(""));
24260         this.nodes = this.el.dom.childNodes;
24261         this.updateIndexes(0);
24262     },
24263
24264     /**
24265      * 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.
24266      * @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:
24267      <pre><code>
24268      view.load({
24269          url: "your-url.php",
24270          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24271          callback: yourFunction,
24272          scope: yourObject, //(optional scope)
24273          discardUrl: false,
24274          nocache: false,
24275          text: "Loading...",
24276          timeout: 30,
24277          scripts: false
24278      });
24279      </code></pre>
24280      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24281      * 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.
24282      * @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}
24283      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24284      * @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.
24285      */
24286     load : function(){
24287         var um = this.el.getUpdateManager();
24288         um.update.apply(um, arguments);
24289     },
24290
24291     render : function(el, response){
24292         this.clearSelections();
24293         this.el.update("");
24294         var o;
24295         try{
24296             o = Roo.util.JSON.decode(response.responseText);
24297             if(this.jsonRoot){
24298                 
24299                 o = o[this.jsonRoot];
24300             }
24301         } catch(e){
24302         }
24303         /**
24304          * The current JSON data or null
24305          */
24306         this.jsonData = o;
24307         this.beforeRender();
24308         this.refresh();
24309     },
24310
24311 /**
24312  * Get the number of records in the current JSON dataset
24313  * @return {Number}
24314  */
24315     getCount : function(){
24316         return this.jsonData ? this.jsonData.length : 0;
24317     },
24318
24319 /**
24320  * Returns the JSON object for the specified node(s)
24321  * @param {HTMLElement/Array} node The node or an array of nodes
24322  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24323  * you get the JSON object for the node
24324  */
24325     getNodeData : function(node){
24326         if(node instanceof Array){
24327             var data = [];
24328             for(var i = 0, len = node.length; i < len; i++){
24329                 data.push(this.getNodeData(node[i]));
24330             }
24331             return data;
24332         }
24333         return this.jsonData[this.indexOf(node)] || null;
24334     },
24335
24336     beforeRender : function(){
24337         this.snapshot = this.jsonData;
24338         if(this.sortInfo){
24339             this.sort.apply(this, this.sortInfo);
24340         }
24341         this.fireEvent("beforerender", this, this.jsonData);
24342     },
24343
24344     onLoad : function(el, o){
24345         this.fireEvent("load", this, this.jsonData, o);
24346     },
24347
24348     onLoadException : function(el, o){
24349         this.fireEvent("loadexception", this, o);
24350     },
24351
24352 /**
24353  * Filter the data by a specific property.
24354  * @param {String} property A property on your JSON objects
24355  * @param {String/RegExp} value Either string that the property values
24356  * should start with, or a RegExp to test against the property
24357  */
24358     filter : function(property, value){
24359         if(this.jsonData){
24360             var data = [];
24361             var ss = this.snapshot;
24362             if(typeof value == "string"){
24363                 var vlen = value.length;
24364                 if(vlen == 0){
24365                     this.clearFilter();
24366                     return;
24367                 }
24368                 value = value.toLowerCase();
24369                 for(var i = 0, len = ss.length; i < len; i++){
24370                     var o = ss[i];
24371                     if(o[property].substr(0, vlen).toLowerCase() == value){
24372                         data.push(o);
24373                     }
24374                 }
24375             } else if(value.exec){ // regex?
24376                 for(var i = 0, len = ss.length; i < len; i++){
24377                     var o = ss[i];
24378                     if(value.test(o[property])){
24379                         data.push(o);
24380                     }
24381                 }
24382             } else{
24383                 return;
24384             }
24385             this.jsonData = data;
24386             this.refresh();
24387         }
24388     },
24389
24390 /**
24391  * Filter by a function. The passed function will be called with each
24392  * object in the current dataset. If the function returns true the value is kept,
24393  * otherwise it is filtered.
24394  * @param {Function} fn
24395  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24396  */
24397     filterBy : function(fn, scope){
24398         if(this.jsonData){
24399             var data = [];
24400             var ss = this.snapshot;
24401             for(var i = 0, len = ss.length; i < len; i++){
24402                 var o = ss[i];
24403                 if(fn.call(scope || this, o)){
24404                     data.push(o);
24405                 }
24406             }
24407             this.jsonData = data;
24408             this.refresh();
24409         }
24410     },
24411
24412 /**
24413  * Clears the current filter.
24414  */
24415     clearFilter : function(){
24416         if(this.snapshot && this.jsonData != this.snapshot){
24417             this.jsonData = this.snapshot;
24418             this.refresh();
24419         }
24420     },
24421
24422
24423 /**
24424  * Sorts the data for this view and refreshes it.
24425  * @param {String} property A property on your JSON objects to sort on
24426  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24427  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24428  */
24429     sort : function(property, dir, sortType){
24430         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24431         if(this.jsonData){
24432             var p = property;
24433             var dsc = dir && dir.toLowerCase() == "desc";
24434             var f = function(o1, o2){
24435                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24436                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24437                 ;
24438                 if(v1 < v2){
24439                     return dsc ? +1 : -1;
24440                 } else if(v1 > v2){
24441                     return dsc ? -1 : +1;
24442                 } else{
24443                     return 0;
24444                 }
24445             };
24446             this.jsonData.sort(f);
24447             this.refresh();
24448             if(this.jsonData != this.snapshot){
24449                 this.snapshot.sort(f);
24450             }
24451         }
24452     }
24453 });/*
24454  * Based on:
24455  * Ext JS Library 1.1.1
24456  * Copyright(c) 2006-2007, Ext JS, LLC.
24457  *
24458  * Originally Released Under LGPL - original licence link has changed is not relivant.
24459  *
24460  * Fork - LGPL
24461  * <script type="text/javascript">
24462  */
24463  
24464
24465 /**
24466  * @class Roo.ColorPalette
24467  * @extends Roo.Component
24468  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24469  * Here's an example of typical usage:
24470  * <pre><code>
24471 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24472 cp.render('my-div');
24473
24474 cp.on('select', function(palette, selColor){
24475     // do something with selColor
24476 });
24477 </code></pre>
24478  * @constructor
24479  * Create a new ColorPalette
24480  * @param {Object} config The config object
24481  */
24482 Roo.ColorPalette = function(config){
24483     Roo.ColorPalette.superclass.constructor.call(this, config);
24484     this.addEvents({
24485         /**
24486              * @event select
24487              * Fires when a color is selected
24488              * @param {ColorPalette} this
24489              * @param {String} color The 6-digit color hex code (without the # symbol)
24490              */
24491         select: true
24492     });
24493
24494     if(this.handler){
24495         this.on("select", this.handler, this.scope, true);
24496     }
24497 };
24498 Roo.extend(Roo.ColorPalette, Roo.Component, {
24499     /**
24500      * @cfg {String} itemCls
24501      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24502      */
24503     itemCls : "x-color-palette",
24504     /**
24505      * @cfg {String} value
24506      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24507      * the hex codes are case-sensitive.
24508      */
24509     value : null,
24510     clickEvent:'click',
24511     // private
24512     ctype: "Roo.ColorPalette",
24513
24514     /**
24515      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24516      */
24517     allowReselect : false,
24518
24519     /**
24520      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24521      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24522      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24523      * of colors with the width setting until the box is symmetrical.</p>
24524      * <p>You can override individual colors if needed:</p>
24525      * <pre><code>
24526 var cp = new Roo.ColorPalette();
24527 cp.colors[0] = "FF0000";  // change the first box to red
24528 </code></pre>
24529
24530 Or you can provide a custom array of your own for complete control:
24531 <pre><code>
24532 var cp = new Roo.ColorPalette();
24533 cp.colors = ["000000", "993300", "333300"];
24534 </code></pre>
24535      * @type Array
24536      */
24537     colors : [
24538         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24539         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24540         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24541         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24542         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24543     ],
24544
24545     // private
24546     onRender : function(container, position){
24547         var t = new Roo.MasterTemplate(
24548             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24549         );
24550         var c = this.colors;
24551         for(var i = 0, len = c.length; i < len; i++){
24552             t.add([c[i]]);
24553         }
24554         var el = document.createElement("div");
24555         el.className = this.itemCls;
24556         t.overwrite(el);
24557         container.dom.insertBefore(el, position);
24558         this.el = Roo.get(el);
24559         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24560         if(this.clickEvent != 'click'){
24561             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24562         }
24563     },
24564
24565     // private
24566     afterRender : function(){
24567         Roo.ColorPalette.superclass.afterRender.call(this);
24568         if(this.value){
24569             var s = this.value;
24570             this.value = null;
24571             this.select(s);
24572         }
24573     },
24574
24575     // private
24576     handleClick : function(e, t){
24577         e.preventDefault();
24578         if(!this.disabled){
24579             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24580             this.select(c.toUpperCase());
24581         }
24582     },
24583
24584     /**
24585      * Selects the specified color in the palette (fires the select event)
24586      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24587      */
24588     select : function(color){
24589         color = color.replace("#", "");
24590         if(color != this.value || this.allowReselect){
24591             var el = this.el;
24592             if(this.value){
24593                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24594             }
24595             el.child("a.color-"+color).addClass("x-color-palette-sel");
24596             this.value = color;
24597             this.fireEvent("select", this, color);
24598         }
24599     }
24600 });/*
24601  * Based on:
24602  * Ext JS Library 1.1.1
24603  * Copyright(c) 2006-2007, Ext JS, LLC.
24604  *
24605  * Originally Released Under LGPL - original licence link has changed is not relivant.
24606  *
24607  * Fork - LGPL
24608  * <script type="text/javascript">
24609  */
24610  
24611 /**
24612  * @class Roo.DatePicker
24613  * @extends Roo.Component
24614  * Simple date picker class.
24615  * @constructor
24616  * Create a new DatePicker
24617  * @param {Object} config The config object
24618  */
24619 Roo.DatePicker = function(config){
24620     Roo.DatePicker.superclass.constructor.call(this, config);
24621
24622     this.value = config && config.value ?
24623                  config.value.clearTime() : new Date().clearTime();
24624
24625     this.addEvents({
24626         /**
24627              * @event select
24628              * Fires when a date is selected
24629              * @param {DatePicker} this
24630              * @param {Date} date The selected date
24631              */
24632         'select': true,
24633         /**
24634              * @event monthchange
24635              * Fires when the displayed month changes 
24636              * @param {DatePicker} this
24637              * @param {Date} date The selected month
24638              */
24639         'monthchange': true
24640     });
24641
24642     if(this.handler){
24643         this.on("select", this.handler,  this.scope || this);
24644     }
24645     // build the disabledDatesRE
24646     if(!this.disabledDatesRE && this.disabledDates){
24647         var dd = this.disabledDates;
24648         var re = "(?:";
24649         for(var i = 0; i < dd.length; i++){
24650             re += dd[i];
24651             if(i != dd.length-1) re += "|";
24652         }
24653         this.disabledDatesRE = new RegExp(re + ")");
24654     }
24655 };
24656
24657 Roo.extend(Roo.DatePicker, Roo.Component, {
24658     /**
24659      * @cfg {String} todayText
24660      * The text to display on the button that selects the current date (defaults to "Today")
24661      */
24662     todayText : "Today",
24663     /**
24664      * @cfg {String} okText
24665      * The text to display on the ok button
24666      */
24667     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24668     /**
24669      * @cfg {String} cancelText
24670      * The text to display on the cancel button
24671      */
24672     cancelText : "Cancel",
24673     /**
24674      * @cfg {String} todayTip
24675      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24676      */
24677     todayTip : "{0} (Spacebar)",
24678     /**
24679      * @cfg {Date} minDate
24680      * Minimum allowable date (JavaScript date object, defaults to null)
24681      */
24682     minDate : null,
24683     /**
24684      * @cfg {Date} maxDate
24685      * Maximum allowable date (JavaScript date object, defaults to null)
24686      */
24687     maxDate : null,
24688     /**
24689      * @cfg {String} minText
24690      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24691      */
24692     minText : "This date is before the minimum date",
24693     /**
24694      * @cfg {String} maxText
24695      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24696      */
24697     maxText : "This date is after the maximum date",
24698     /**
24699      * @cfg {String} format
24700      * The default date format string which can be overriden for localization support.  The format must be
24701      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24702      */
24703     format : "m/d/y",
24704     /**
24705      * @cfg {Array} disabledDays
24706      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24707      */
24708     disabledDays : null,
24709     /**
24710      * @cfg {String} disabledDaysText
24711      * The tooltip to display when the date falls on a disabled day (defaults to "")
24712      */
24713     disabledDaysText : "",
24714     /**
24715      * @cfg {RegExp} disabledDatesRE
24716      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24717      */
24718     disabledDatesRE : null,
24719     /**
24720      * @cfg {String} disabledDatesText
24721      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24722      */
24723     disabledDatesText : "",
24724     /**
24725      * @cfg {Boolean} constrainToViewport
24726      * True to constrain the date picker to the viewport (defaults to true)
24727      */
24728     constrainToViewport : true,
24729     /**
24730      * @cfg {Array} monthNames
24731      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24732      */
24733     monthNames : Date.monthNames,
24734     /**
24735      * @cfg {Array} dayNames
24736      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24737      */
24738     dayNames : Date.dayNames,
24739     /**
24740      * @cfg {String} nextText
24741      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24742      */
24743     nextText: 'Next Month (Control+Right)',
24744     /**
24745      * @cfg {String} prevText
24746      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24747      */
24748     prevText: 'Previous Month (Control+Left)',
24749     /**
24750      * @cfg {String} monthYearText
24751      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24752      */
24753     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24754     /**
24755      * @cfg {Number} startDay
24756      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24757      */
24758     startDay : 0,
24759     /**
24760      * @cfg {Bool} showClear
24761      * Show a clear button (usefull for date form elements that can be blank.)
24762      */
24763     
24764     showClear: false,
24765     
24766     /**
24767      * Sets the value of the date field
24768      * @param {Date} value The date to set
24769      */
24770     setValue : function(value){
24771         var old = this.value;
24772         this.value = value.clearTime(true);
24773         if(this.el){
24774             this.update(this.value);
24775         }
24776     },
24777
24778     /**
24779      * Gets the current selected value of the date field
24780      * @return {Date} The selected date
24781      */
24782     getValue : function(){
24783         return this.value;
24784     },
24785
24786     // private
24787     focus : function(){
24788         if(this.el){
24789             this.update(this.activeDate);
24790         }
24791     },
24792
24793     // private
24794     onRender : function(container, position){
24795         var m = [
24796              '<table cellspacing="0">',
24797                 '<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>',
24798                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24799         var dn = this.dayNames;
24800         for(var i = 0; i < 7; i++){
24801             var d = this.startDay+i;
24802             if(d > 6){
24803                 d = d-7;
24804             }
24805             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24806         }
24807         m[m.length] = "</tr></thead><tbody><tr>";
24808         for(var i = 0; i < 42; i++) {
24809             if(i % 7 == 0 && i != 0){
24810                 m[m.length] = "</tr><tr>";
24811             }
24812             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24813         }
24814         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24815             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24816
24817         var el = document.createElement("div");
24818         el.className = "x-date-picker";
24819         el.innerHTML = m.join("");
24820
24821         container.dom.insertBefore(el, position);
24822
24823         this.el = Roo.get(el);
24824         this.eventEl = Roo.get(el.firstChild);
24825
24826         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24827             handler: this.showPrevMonth,
24828             scope: this,
24829             preventDefault:true,
24830             stopDefault:true
24831         });
24832
24833         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24834             handler: this.showNextMonth,
24835             scope: this,
24836             preventDefault:true,
24837             stopDefault:true
24838         });
24839
24840         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24841
24842         this.monthPicker = this.el.down('div.x-date-mp');
24843         this.monthPicker.enableDisplayMode('block');
24844         
24845         var kn = new Roo.KeyNav(this.eventEl, {
24846             "left" : function(e){
24847                 e.ctrlKey ?
24848                     this.showPrevMonth() :
24849                     this.update(this.activeDate.add("d", -1));
24850             },
24851
24852             "right" : function(e){
24853                 e.ctrlKey ?
24854                     this.showNextMonth() :
24855                     this.update(this.activeDate.add("d", 1));
24856             },
24857
24858             "up" : function(e){
24859                 e.ctrlKey ?
24860                     this.showNextYear() :
24861                     this.update(this.activeDate.add("d", -7));
24862             },
24863
24864             "down" : function(e){
24865                 e.ctrlKey ?
24866                     this.showPrevYear() :
24867                     this.update(this.activeDate.add("d", 7));
24868             },
24869
24870             "pageUp" : function(e){
24871                 this.showNextMonth();
24872             },
24873
24874             "pageDown" : function(e){
24875                 this.showPrevMonth();
24876             },
24877
24878             "enter" : function(e){
24879                 e.stopPropagation();
24880                 return true;
24881             },
24882
24883             scope : this
24884         });
24885
24886         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24887
24888         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24889
24890         this.el.unselectable();
24891         
24892         this.cells = this.el.select("table.x-date-inner tbody td");
24893         this.textNodes = this.el.query("table.x-date-inner tbody span");
24894
24895         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24896             text: "&#160;",
24897             tooltip: this.monthYearText
24898         });
24899
24900         this.mbtn.on('click', this.showMonthPicker, this);
24901         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24902
24903
24904         var today = (new Date()).dateFormat(this.format);
24905         
24906         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24907         if (this.showClear) {
24908             baseTb.add( new Roo.Toolbar.Fill());
24909         }
24910         baseTb.add({
24911             text: String.format(this.todayText, today),
24912             tooltip: String.format(this.todayTip, today),
24913             handler: this.selectToday,
24914             scope: this
24915         });
24916         
24917         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24918             
24919         //});
24920         if (this.showClear) {
24921             
24922             baseTb.add( new Roo.Toolbar.Fill());
24923             baseTb.add({
24924                 text: '&#160;',
24925                 cls: 'x-btn-icon x-btn-clear',
24926                 handler: function() {
24927                     //this.value = '';
24928                     this.fireEvent("select", this, '');
24929                 },
24930                 scope: this
24931             });
24932         }
24933         
24934         
24935         if(Roo.isIE){
24936             this.el.repaint();
24937         }
24938         this.update(this.value);
24939     },
24940
24941     createMonthPicker : function(){
24942         if(!this.monthPicker.dom.firstChild){
24943             var buf = ['<table border="0" cellspacing="0">'];
24944             for(var i = 0; i < 6; i++){
24945                 buf.push(
24946                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24947                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24948                     i == 0 ?
24949                     '<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>' :
24950                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24951                 );
24952             }
24953             buf.push(
24954                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24955                     this.okText,
24956                     '</button><button type="button" class="x-date-mp-cancel">',
24957                     this.cancelText,
24958                     '</button></td></tr>',
24959                 '</table>'
24960             );
24961             this.monthPicker.update(buf.join(''));
24962             this.monthPicker.on('click', this.onMonthClick, this);
24963             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24964
24965             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24966             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24967
24968             this.mpMonths.each(function(m, a, i){
24969                 i += 1;
24970                 if((i%2) == 0){
24971                     m.dom.xmonth = 5 + Math.round(i * .5);
24972                 }else{
24973                     m.dom.xmonth = Math.round((i-1) * .5);
24974                 }
24975             });
24976         }
24977     },
24978
24979     showMonthPicker : function(){
24980         this.createMonthPicker();
24981         var size = this.el.getSize();
24982         this.monthPicker.setSize(size);
24983         this.monthPicker.child('table').setSize(size);
24984
24985         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24986         this.updateMPMonth(this.mpSelMonth);
24987         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24988         this.updateMPYear(this.mpSelYear);
24989
24990         this.monthPicker.slideIn('t', {duration:.2});
24991     },
24992
24993     updateMPYear : function(y){
24994         this.mpyear = y;
24995         var ys = this.mpYears.elements;
24996         for(var i = 1; i <= 10; i++){
24997             var td = ys[i-1], y2;
24998             if((i%2) == 0){
24999                 y2 = y + Math.round(i * .5);
25000                 td.firstChild.innerHTML = y2;
25001                 td.xyear = y2;
25002             }else{
25003                 y2 = y - (5-Math.round(i * .5));
25004                 td.firstChild.innerHTML = y2;
25005                 td.xyear = y2;
25006             }
25007             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
25008         }
25009     },
25010
25011     updateMPMonth : function(sm){
25012         this.mpMonths.each(function(m, a, i){
25013             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
25014         });
25015     },
25016
25017     selectMPMonth: function(m){
25018         
25019     },
25020
25021     onMonthClick : function(e, t){
25022         e.stopEvent();
25023         var el = new Roo.Element(t), pn;
25024         if(el.is('button.x-date-mp-cancel')){
25025             this.hideMonthPicker();
25026         }
25027         else if(el.is('button.x-date-mp-ok')){
25028             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25029             this.hideMonthPicker();
25030         }
25031         else if(pn = el.up('td.x-date-mp-month', 2)){
25032             this.mpMonths.removeClass('x-date-mp-sel');
25033             pn.addClass('x-date-mp-sel');
25034             this.mpSelMonth = pn.dom.xmonth;
25035         }
25036         else if(pn = el.up('td.x-date-mp-year', 2)){
25037             this.mpYears.removeClass('x-date-mp-sel');
25038             pn.addClass('x-date-mp-sel');
25039             this.mpSelYear = pn.dom.xyear;
25040         }
25041         else if(el.is('a.x-date-mp-prev')){
25042             this.updateMPYear(this.mpyear-10);
25043         }
25044         else if(el.is('a.x-date-mp-next')){
25045             this.updateMPYear(this.mpyear+10);
25046         }
25047     },
25048
25049     onMonthDblClick : function(e, t){
25050         e.stopEvent();
25051         var el = new Roo.Element(t), pn;
25052         if(pn = el.up('td.x-date-mp-month', 2)){
25053             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
25054             this.hideMonthPicker();
25055         }
25056         else if(pn = el.up('td.x-date-mp-year', 2)){
25057             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
25058             this.hideMonthPicker();
25059         }
25060     },
25061
25062     hideMonthPicker : function(disableAnim){
25063         if(this.monthPicker){
25064             if(disableAnim === true){
25065                 this.monthPicker.hide();
25066             }else{
25067                 this.monthPicker.slideOut('t', {duration:.2});
25068             }
25069         }
25070     },
25071
25072     // private
25073     showPrevMonth : function(e){
25074         this.update(this.activeDate.add("mo", -1));
25075     },
25076
25077     // private
25078     showNextMonth : function(e){
25079         this.update(this.activeDate.add("mo", 1));
25080     },
25081
25082     // private
25083     showPrevYear : function(){
25084         this.update(this.activeDate.add("y", -1));
25085     },
25086
25087     // private
25088     showNextYear : function(){
25089         this.update(this.activeDate.add("y", 1));
25090     },
25091
25092     // private
25093     handleMouseWheel : function(e){
25094         var delta = e.getWheelDelta();
25095         if(delta > 0){
25096             this.showPrevMonth();
25097             e.stopEvent();
25098         } else if(delta < 0){
25099             this.showNextMonth();
25100             e.stopEvent();
25101         }
25102     },
25103
25104     // private
25105     handleDateClick : function(e, t){
25106         e.stopEvent();
25107         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
25108             this.setValue(new Date(t.dateValue));
25109             this.fireEvent("select", this, this.value);
25110         }
25111     },
25112
25113     // private
25114     selectToday : function(){
25115         this.setValue(new Date().clearTime());
25116         this.fireEvent("select", this, this.value);
25117     },
25118
25119     // private
25120     update : function(date)
25121     {
25122         var vd = this.activeDate;
25123         this.activeDate = date;
25124         if(vd && this.el){
25125             var t = date.getTime();
25126             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
25127                 this.cells.removeClass("x-date-selected");
25128                 this.cells.each(function(c){
25129                    if(c.dom.firstChild.dateValue == t){
25130                        c.addClass("x-date-selected");
25131                        setTimeout(function(){
25132                             try{c.dom.firstChild.focus();}catch(e){}
25133                        }, 50);
25134                        return false;
25135                    }
25136                 });
25137                 return;
25138             }
25139         }
25140         
25141         var days = date.getDaysInMonth();
25142         var firstOfMonth = date.getFirstDateOfMonth();
25143         var startingPos = firstOfMonth.getDay()-this.startDay;
25144
25145         if(startingPos <= this.startDay){
25146             startingPos += 7;
25147         }
25148
25149         var pm = date.add("mo", -1);
25150         var prevStart = pm.getDaysInMonth()-startingPos;
25151
25152         var cells = this.cells.elements;
25153         var textEls = this.textNodes;
25154         days += startingPos;
25155
25156         // convert everything to numbers so it's fast
25157         var day = 86400000;
25158         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
25159         var today = new Date().clearTime().getTime();
25160         var sel = date.clearTime().getTime();
25161         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
25162         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
25163         var ddMatch = this.disabledDatesRE;
25164         var ddText = this.disabledDatesText;
25165         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
25166         var ddaysText = this.disabledDaysText;
25167         var format = this.format;
25168
25169         var setCellClass = function(cal, cell){
25170             cell.title = "";
25171             var t = d.getTime();
25172             cell.firstChild.dateValue = t;
25173             if(t == today){
25174                 cell.className += " x-date-today";
25175                 cell.title = cal.todayText;
25176             }
25177             if(t == sel){
25178                 cell.className += " x-date-selected";
25179                 setTimeout(function(){
25180                     try{cell.firstChild.focus();}catch(e){}
25181                 }, 50);
25182             }
25183             // disabling
25184             if(t < min) {
25185                 cell.className = " x-date-disabled";
25186                 cell.title = cal.minText;
25187                 return;
25188             }
25189             if(t > max) {
25190                 cell.className = " x-date-disabled";
25191                 cell.title = cal.maxText;
25192                 return;
25193             }
25194             if(ddays){
25195                 if(ddays.indexOf(d.getDay()) != -1){
25196                     cell.title = ddaysText;
25197                     cell.className = " x-date-disabled";
25198                 }
25199             }
25200             if(ddMatch && format){
25201                 var fvalue = d.dateFormat(format);
25202                 if(ddMatch.test(fvalue)){
25203                     cell.title = ddText.replace("%0", fvalue);
25204                     cell.className = " x-date-disabled";
25205                 }
25206             }
25207         };
25208
25209         var i = 0;
25210         for(; i < startingPos; i++) {
25211             textEls[i].innerHTML = (++prevStart);
25212             d.setDate(d.getDate()+1);
25213             cells[i].className = "x-date-prevday";
25214             setCellClass(this, cells[i]);
25215         }
25216         for(; i < days; i++){
25217             intDay = i - startingPos + 1;
25218             textEls[i].innerHTML = (intDay);
25219             d.setDate(d.getDate()+1);
25220             cells[i].className = "x-date-active";
25221             setCellClass(this, cells[i]);
25222         }
25223         var extraDays = 0;
25224         for(; i < 42; i++) {
25225              textEls[i].innerHTML = (++extraDays);
25226              d.setDate(d.getDate()+1);
25227              cells[i].className = "x-date-nextday";
25228              setCellClass(this, cells[i]);
25229         }
25230
25231         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25232         this.fireEvent('monthchange', this, date);
25233         
25234         if(!this.internalRender){
25235             var main = this.el.dom.firstChild;
25236             var w = main.offsetWidth;
25237             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25238             Roo.fly(main).setWidth(w);
25239             this.internalRender = true;
25240             // opera does not respect the auto grow header center column
25241             // then, after it gets a width opera refuses to recalculate
25242             // without a second pass
25243             if(Roo.isOpera && !this.secondPass){
25244                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25245                 this.secondPass = true;
25246                 this.update.defer(10, this, [date]);
25247             }
25248         }
25249         
25250         
25251     }
25252 });        /*
25253  * Based on:
25254  * Ext JS Library 1.1.1
25255  * Copyright(c) 2006-2007, Ext JS, LLC.
25256  *
25257  * Originally Released Under LGPL - original licence link has changed is not relivant.
25258  *
25259  * Fork - LGPL
25260  * <script type="text/javascript">
25261  */
25262 /**
25263  * @class Roo.TabPanel
25264  * @extends Roo.util.Observable
25265  * A lightweight tab container.
25266  * <br><br>
25267  * Usage:
25268  * <pre><code>
25269 // basic tabs 1, built from existing content
25270 var tabs = new Roo.TabPanel("tabs1");
25271 tabs.addTab("script", "View Script");
25272 tabs.addTab("markup", "View Markup");
25273 tabs.activate("script");
25274
25275 // more advanced tabs, built from javascript
25276 var jtabs = new Roo.TabPanel("jtabs");
25277 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25278
25279 // set up the UpdateManager
25280 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25281 var updater = tab2.getUpdateManager();
25282 updater.setDefaultUrl("ajax1.htm");
25283 tab2.on('activate', updater.refresh, updater, true);
25284
25285 // Use setUrl for Ajax loading
25286 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25287 tab3.setUrl("ajax2.htm", null, true);
25288
25289 // Disabled tab
25290 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25291 tab4.disable();
25292
25293 jtabs.activate("jtabs-1");
25294  * </code></pre>
25295  * @constructor
25296  * Create a new TabPanel.
25297  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25298  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25299  */
25300 Roo.TabPanel = function(container, config){
25301     /**
25302     * The container element for this TabPanel.
25303     * @type Roo.Element
25304     */
25305     this.el = Roo.get(container, true);
25306     if(config){
25307         if(typeof config == "boolean"){
25308             this.tabPosition = config ? "bottom" : "top";
25309         }else{
25310             Roo.apply(this, config);
25311         }
25312     }
25313     if(this.tabPosition == "bottom"){
25314         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25315         this.el.addClass("x-tabs-bottom");
25316     }
25317     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25318     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25319     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25320     if(Roo.isIE){
25321         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25322     }
25323     if(this.tabPosition != "bottom"){
25324         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
25325          * @type Roo.Element
25326          */
25327         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25328         this.el.addClass("x-tabs-top");
25329     }
25330     this.items = [];
25331
25332     this.bodyEl.setStyle("position", "relative");
25333
25334     this.active = null;
25335     this.activateDelegate = this.activate.createDelegate(this);
25336
25337     this.addEvents({
25338         /**
25339          * @event tabchange
25340          * Fires when the active tab changes
25341          * @param {Roo.TabPanel} this
25342          * @param {Roo.TabPanelItem} activePanel The new active tab
25343          */
25344         "tabchange": true,
25345         /**
25346          * @event beforetabchange
25347          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25348          * @param {Roo.TabPanel} this
25349          * @param {Object} e Set cancel to true on this object to cancel the tab change
25350          * @param {Roo.TabPanelItem} tab The tab being changed to
25351          */
25352         "beforetabchange" : true
25353     });
25354
25355     Roo.EventManager.onWindowResize(this.onResize, this);
25356     this.cpad = this.el.getPadding("lr");
25357     this.hiddenCount = 0;
25358
25359
25360     // toolbar on the tabbar support...
25361     if (this.toolbar) {
25362         var tcfg = this.toolbar;
25363         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
25364         this.toolbar = new Roo.Toolbar(tcfg);
25365         if (Roo.isSafari) {
25366             var tbl = tcfg.container.child('table', true);
25367             tbl.setAttribute('width', '100%');
25368         }
25369         
25370     }
25371    
25372
25373
25374     Roo.TabPanel.superclass.constructor.call(this);
25375 };
25376
25377 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25378     /*
25379      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25380      */
25381     tabPosition : "top",
25382     /*
25383      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25384      */
25385     currentTabWidth : 0,
25386     /*
25387      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25388      */
25389     minTabWidth : 40,
25390     /*
25391      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25392      */
25393     maxTabWidth : 250,
25394     /*
25395      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25396      */
25397     preferredTabWidth : 175,
25398     /*
25399      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25400      */
25401     resizeTabs : false,
25402     /*
25403      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25404      */
25405     monitorResize : true,
25406     /*
25407      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
25408      */
25409     toolbar : false,
25410
25411     /**
25412      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25413      * @param {String} id The id of the div to use <b>or create</b>
25414      * @param {String} text The text for the tab
25415      * @param {String} content (optional) Content to put in the TabPanelItem body
25416      * @param {Boolean} closable (optional) True to create a close icon on the tab
25417      * @return {Roo.TabPanelItem} The created TabPanelItem
25418      */
25419     addTab : function(id, text, content, closable){
25420         var item = new Roo.TabPanelItem(this, id, text, closable);
25421         this.addTabItem(item);
25422         if(content){
25423             item.setContent(content);
25424         }
25425         return item;
25426     },
25427
25428     /**
25429      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25430      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25431      * @return {Roo.TabPanelItem}
25432      */
25433     getTab : function(id){
25434         return this.items[id];
25435     },
25436
25437     /**
25438      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25439      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25440      */
25441     hideTab : function(id){
25442         var t = this.items[id];
25443         if(!t.isHidden()){
25444            t.setHidden(true);
25445            this.hiddenCount++;
25446            this.autoSizeTabs();
25447         }
25448     },
25449
25450     /**
25451      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25452      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25453      */
25454     unhideTab : function(id){
25455         var t = this.items[id];
25456         if(t.isHidden()){
25457            t.setHidden(false);
25458            this.hiddenCount--;
25459            this.autoSizeTabs();
25460         }
25461     },
25462
25463     /**
25464      * Adds an existing {@link Roo.TabPanelItem}.
25465      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25466      */
25467     addTabItem : function(item){
25468         this.items[item.id] = item;
25469         this.items.push(item);
25470         if(this.resizeTabs){
25471            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25472            this.autoSizeTabs();
25473         }else{
25474             item.autoSize();
25475         }
25476     },
25477
25478     /**
25479      * Removes a {@link Roo.TabPanelItem}.
25480      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25481      */
25482     removeTab : function(id){
25483         var items = this.items;
25484         var tab = items[id];
25485         if(!tab) { return; }
25486         var index = items.indexOf(tab);
25487         if(this.active == tab && items.length > 1){
25488             var newTab = this.getNextAvailable(index);
25489             if(newTab) {
25490                 newTab.activate();
25491             }
25492         }
25493         this.stripEl.dom.removeChild(tab.pnode.dom);
25494         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25495             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25496         }
25497         items.splice(index, 1);
25498         delete this.items[tab.id];
25499         tab.fireEvent("close", tab);
25500         tab.purgeListeners();
25501         this.autoSizeTabs();
25502     },
25503
25504     getNextAvailable : function(start){
25505         var items = this.items;
25506         var index = start;
25507         // look for a next tab that will slide over to
25508         // replace the one being removed
25509         while(index < items.length){
25510             var item = items[++index];
25511             if(item && !item.isHidden()){
25512                 return item;
25513             }
25514         }
25515         // if one isn't found select the previous tab (on the left)
25516         index = start;
25517         while(index >= 0){
25518             var item = items[--index];
25519             if(item && !item.isHidden()){
25520                 return item;
25521             }
25522         }
25523         return null;
25524     },
25525
25526     /**
25527      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25528      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25529      */
25530     disableTab : function(id){
25531         var tab = this.items[id];
25532         if(tab && this.active != tab){
25533             tab.disable();
25534         }
25535     },
25536
25537     /**
25538      * Enables a {@link Roo.TabPanelItem} that is disabled.
25539      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25540      */
25541     enableTab : function(id){
25542         var tab = this.items[id];
25543         tab.enable();
25544     },
25545
25546     /**
25547      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25548      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25549      * @return {Roo.TabPanelItem} The TabPanelItem.
25550      */
25551     activate : function(id){
25552         var tab = this.items[id];
25553         if(!tab){
25554             return null;
25555         }
25556         if(tab == this.active || tab.disabled){
25557             return tab;
25558         }
25559         var e = {};
25560         this.fireEvent("beforetabchange", this, e, tab);
25561         if(e.cancel !== true && !tab.disabled){
25562             if(this.active){
25563                 this.active.hide();
25564             }
25565             this.active = this.items[id];
25566             this.active.show();
25567             this.fireEvent("tabchange", this, this.active);
25568         }
25569         return tab;
25570     },
25571
25572     /**
25573      * Gets the active {@link Roo.TabPanelItem}.
25574      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25575      */
25576     getActiveTab : function(){
25577         return this.active;
25578     },
25579
25580     /**
25581      * Updates the tab body element to fit the height of the container element
25582      * for overflow scrolling
25583      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25584      */
25585     syncHeight : function(targetHeight){
25586         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25587         var bm = this.bodyEl.getMargins();
25588         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25589         this.bodyEl.setHeight(newHeight);
25590         return newHeight;
25591     },
25592
25593     onResize : function(){
25594         if(this.monitorResize){
25595             this.autoSizeTabs();
25596         }
25597     },
25598
25599     /**
25600      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25601      */
25602     beginUpdate : function(){
25603         this.updating = true;
25604     },
25605
25606     /**
25607      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25608      */
25609     endUpdate : function(){
25610         this.updating = false;
25611         this.autoSizeTabs();
25612     },
25613
25614     /**
25615      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25616      */
25617     autoSizeTabs : function(){
25618         var count = this.items.length;
25619         var vcount = count - this.hiddenCount;
25620         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25621         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25622         var availWidth = Math.floor(w / vcount);
25623         var b = this.stripBody;
25624         if(b.getWidth() > w){
25625             var tabs = this.items;
25626             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25627             if(availWidth < this.minTabWidth){
25628                 /*if(!this.sleft){    // incomplete scrolling code
25629                     this.createScrollButtons();
25630                 }
25631                 this.showScroll();
25632                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25633             }
25634         }else{
25635             if(this.currentTabWidth < this.preferredTabWidth){
25636                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25637             }
25638         }
25639     },
25640
25641     /**
25642      * Returns the number of tabs in this TabPanel.
25643      * @return {Number}
25644      */
25645      getCount : function(){
25646          return this.items.length;
25647      },
25648
25649     /**
25650      * Resizes all the tabs to the passed width
25651      * @param {Number} The new width
25652      */
25653     setTabWidth : function(width){
25654         this.currentTabWidth = width;
25655         for(var i = 0, len = this.items.length; i < len; i++) {
25656                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25657         }
25658     },
25659
25660     /**
25661      * Destroys this TabPanel
25662      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25663      */
25664     destroy : function(removeEl){
25665         Roo.EventManager.removeResizeListener(this.onResize, this);
25666         for(var i = 0, len = this.items.length; i < len; i++){
25667             this.items[i].purgeListeners();
25668         }
25669         if(removeEl === true){
25670             this.el.update("");
25671             this.el.remove();
25672         }
25673     }
25674 });
25675
25676 /**
25677  * @class Roo.TabPanelItem
25678  * @extends Roo.util.Observable
25679  * Represents an individual item (tab plus body) in a TabPanel.
25680  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25681  * @param {String} id The id of this TabPanelItem
25682  * @param {String} text The text for the tab of this TabPanelItem
25683  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25684  */
25685 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25686     /**
25687      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25688      * @type Roo.TabPanel
25689      */
25690     this.tabPanel = tabPanel;
25691     /**
25692      * The id for this TabPanelItem
25693      * @type String
25694      */
25695     this.id = id;
25696     /** @private */
25697     this.disabled = false;
25698     /** @private */
25699     this.text = text;
25700     /** @private */
25701     this.loaded = false;
25702     this.closable = closable;
25703
25704     /**
25705      * The body element for this TabPanelItem.
25706      * @type Roo.Element
25707      */
25708     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25709     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25710     this.bodyEl.setStyle("display", "block");
25711     this.bodyEl.setStyle("zoom", "1");
25712     this.hideAction();
25713
25714     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25715     /** @private */
25716     this.el = Roo.get(els.el, true);
25717     this.inner = Roo.get(els.inner, true);
25718     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25719     this.pnode = Roo.get(els.el.parentNode, true);
25720     this.el.on("mousedown", this.onTabMouseDown, this);
25721     this.el.on("click", this.onTabClick, this);
25722     /** @private */
25723     if(closable){
25724         var c = Roo.get(els.close, true);
25725         c.dom.title = this.closeText;
25726         c.addClassOnOver("close-over");
25727         c.on("click", this.closeClick, this);
25728      }
25729
25730     this.addEvents({
25731          /**
25732          * @event activate
25733          * Fires when this tab becomes the active tab.
25734          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25735          * @param {Roo.TabPanelItem} this
25736          */
25737         "activate": true,
25738         /**
25739          * @event beforeclose
25740          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25741          * @param {Roo.TabPanelItem} this
25742          * @param {Object} e Set cancel to true on this object to cancel the close.
25743          */
25744         "beforeclose": true,
25745         /**
25746          * @event close
25747          * Fires when this tab is closed.
25748          * @param {Roo.TabPanelItem} this
25749          */
25750          "close": true,
25751         /**
25752          * @event deactivate
25753          * Fires when this tab is no longer the active tab.
25754          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25755          * @param {Roo.TabPanelItem} this
25756          */
25757          "deactivate" : true
25758     });
25759     this.hidden = false;
25760
25761     Roo.TabPanelItem.superclass.constructor.call(this);
25762 };
25763
25764 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25765     purgeListeners : function(){
25766        Roo.util.Observable.prototype.purgeListeners.call(this);
25767        this.el.removeAllListeners();
25768     },
25769     /**
25770      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25771      */
25772     show : function(){
25773         this.pnode.addClass("on");
25774         this.showAction();
25775         if(Roo.isOpera){
25776             this.tabPanel.stripWrap.repaint();
25777         }
25778         this.fireEvent("activate", this.tabPanel, this);
25779     },
25780
25781     /**
25782      * Returns true if this tab is the active tab.
25783      * @return {Boolean}
25784      */
25785     isActive : function(){
25786         return this.tabPanel.getActiveTab() == this;
25787     },
25788
25789     /**
25790      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25791      */
25792     hide : function(){
25793         this.pnode.removeClass("on");
25794         this.hideAction();
25795         this.fireEvent("deactivate", this.tabPanel, this);
25796     },
25797
25798     hideAction : function(){
25799         this.bodyEl.hide();
25800         this.bodyEl.setStyle("position", "absolute");
25801         this.bodyEl.setLeft("-20000px");
25802         this.bodyEl.setTop("-20000px");
25803     },
25804
25805     showAction : function(){
25806         this.bodyEl.setStyle("position", "relative");
25807         this.bodyEl.setTop("");
25808         this.bodyEl.setLeft("");
25809         this.bodyEl.show();
25810     },
25811
25812     /**
25813      * Set the tooltip for the tab.
25814      * @param {String} tooltip The tab's tooltip
25815      */
25816     setTooltip : function(text){
25817         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25818             this.textEl.dom.qtip = text;
25819             this.textEl.dom.removeAttribute('title');
25820         }else{
25821             this.textEl.dom.title = text;
25822         }
25823     },
25824
25825     onTabClick : function(e){
25826         e.preventDefault();
25827         this.tabPanel.activate(this.id);
25828     },
25829
25830     onTabMouseDown : function(e){
25831         e.preventDefault();
25832         this.tabPanel.activate(this.id);
25833     },
25834
25835     getWidth : function(){
25836         return this.inner.getWidth();
25837     },
25838
25839     setWidth : function(width){
25840         var iwidth = width - this.pnode.getPadding("lr");
25841         this.inner.setWidth(iwidth);
25842         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25843         this.pnode.setWidth(width);
25844     },
25845
25846     /**
25847      * Show or hide the tab
25848      * @param {Boolean} hidden True to hide or false to show.
25849      */
25850     setHidden : function(hidden){
25851         this.hidden = hidden;
25852         this.pnode.setStyle("display", hidden ? "none" : "");
25853     },
25854
25855     /**
25856      * Returns true if this tab is "hidden"
25857      * @return {Boolean}
25858      */
25859     isHidden : function(){
25860         return this.hidden;
25861     },
25862
25863     /**
25864      * Returns the text for this tab
25865      * @return {String}
25866      */
25867     getText : function(){
25868         return this.text;
25869     },
25870
25871     autoSize : function(){
25872         //this.el.beginMeasure();
25873         this.textEl.setWidth(1);
25874         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25875         //this.el.endMeasure();
25876     },
25877
25878     /**
25879      * Sets the text for the tab (Note: this also sets the tooltip text)
25880      * @param {String} text The tab's text and tooltip
25881      */
25882     setText : function(text){
25883         this.text = text;
25884         this.textEl.update(text);
25885         this.setTooltip(text);
25886         if(!this.tabPanel.resizeTabs){
25887             this.autoSize();
25888         }
25889     },
25890     /**
25891      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25892      */
25893     activate : function(){
25894         this.tabPanel.activate(this.id);
25895     },
25896
25897     /**
25898      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25899      */
25900     disable : function(){
25901         if(this.tabPanel.active != this){
25902             this.disabled = true;
25903             this.pnode.addClass("disabled");
25904         }
25905     },
25906
25907     /**
25908      * Enables this TabPanelItem if it was previously disabled.
25909      */
25910     enable : function(){
25911         this.disabled = false;
25912         this.pnode.removeClass("disabled");
25913     },
25914
25915     /**
25916      * Sets the content for this TabPanelItem.
25917      * @param {String} content The content
25918      * @param {Boolean} loadScripts true to look for and load scripts
25919      */
25920     setContent : function(content, loadScripts){
25921         this.bodyEl.update(content, loadScripts);
25922     },
25923
25924     /**
25925      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25926      * @return {Roo.UpdateManager} The UpdateManager
25927      */
25928     getUpdateManager : function(){
25929         return this.bodyEl.getUpdateManager();
25930     },
25931
25932     /**
25933      * Set a URL to be used to load the content for this TabPanelItem.
25934      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25935      * @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)
25936      * @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)
25937      * @return {Roo.UpdateManager} The UpdateManager
25938      */
25939     setUrl : function(url, params, loadOnce){
25940         if(this.refreshDelegate){
25941             this.un('activate', this.refreshDelegate);
25942         }
25943         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25944         this.on("activate", this.refreshDelegate);
25945         return this.bodyEl.getUpdateManager();
25946     },
25947
25948     /** @private */
25949     _handleRefresh : function(url, params, loadOnce){
25950         if(!loadOnce || !this.loaded){
25951             var updater = this.bodyEl.getUpdateManager();
25952             updater.update(url, params, this._setLoaded.createDelegate(this));
25953         }
25954     },
25955
25956     /**
25957      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25958      *   Will fail silently if the setUrl method has not been called.
25959      *   This does not activate the panel, just updates its content.
25960      */
25961     refresh : function(){
25962         if(this.refreshDelegate){
25963            this.loaded = false;
25964            this.refreshDelegate();
25965         }
25966     },
25967
25968     /** @private */
25969     _setLoaded : function(){
25970         this.loaded = true;
25971     },
25972
25973     /** @private */
25974     closeClick : function(e){
25975         var o = {};
25976         e.stopEvent();
25977         this.fireEvent("beforeclose", this, o);
25978         if(o.cancel !== true){
25979             this.tabPanel.removeTab(this.id);
25980         }
25981     },
25982     /**
25983      * The text displayed in the tooltip for the close icon.
25984      * @type String
25985      */
25986     closeText : "Close this tab"
25987 });
25988
25989 /** @private */
25990 Roo.TabPanel.prototype.createStrip = function(container){
25991     var strip = document.createElement("div");
25992     strip.className = "x-tabs-wrap";
25993     container.appendChild(strip);
25994     return strip;
25995 };
25996 /** @private */
25997 Roo.TabPanel.prototype.createStripList = function(strip){
25998     // div wrapper for retard IE
25999     // returns the "tr" element.
26000     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
26001         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
26002         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
26003     return strip.firstChild.firstChild.firstChild.firstChild;
26004 };
26005 /** @private */
26006 Roo.TabPanel.prototype.createBody = function(container){
26007     var body = document.createElement("div");
26008     Roo.id(body, "tab-body");
26009     Roo.fly(body).addClass("x-tabs-body");
26010     container.appendChild(body);
26011     return body;
26012 };
26013 /** @private */
26014 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
26015     var body = Roo.getDom(id);
26016     if(!body){
26017         body = document.createElement("div");
26018         body.id = id;
26019     }
26020     Roo.fly(body).addClass("x-tabs-item-body");
26021     bodyEl.insertBefore(body, bodyEl.firstChild);
26022     return body;
26023 };
26024 /** @private */
26025 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
26026     var td = document.createElement("td");
26027     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
26028     //stripEl.appendChild(td);
26029     if(closable){
26030         td.className = "x-tabs-closable";
26031         if(!this.closeTpl){
26032             this.closeTpl = new Roo.Template(
26033                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26034                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
26035                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
26036             );
26037         }
26038         var el = this.closeTpl.overwrite(td, {"text": text});
26039         var close = el.getElementsByTagName("div")[0];
26040         var inner = el.getElementsByTagName("em")[0];
26041         return {"el": el, "close": close, "inner": inner};
26042     } else {
26043         if(!this.tabTpl){
26044             this.tabTpl = new Roo.Template(
26045                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
26046                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
26047             );
26048         }
26049         var el = this.tabTpl.overwrite(td, {"text": text});
26050         var inner = el.getElementsByTagName("em")[0];
26051         return {"el": el, "inner": inner};
26052     }
26053 };/*
26054  * Based on:
26055  * Ext JS Library 1.1.1
26056  * Copyright(c) 2006-2007, Ext JS, LLC.
26057  *
26058  * Originally Released Under LGPL - original licence link has changed is not relivant.
26059  *
26060  * Fork - LGPL
26061  * <script type="text/javascript">
26062  */
26063
26064 /**
26065  * @class Roo.Button
26066  * @extends Roo.util.Observable
26067  * Simple Button class
26068  * @cfg {String} text The button text
26069  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
26070  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
26071  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
26072  * @cfg {Object} scope The scope of the handler
26073  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
26074  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
26075  * @cfg {Boolean} hidden True to start hidden (defaults to false)
26076  * @cfg {Boolean} disabled True to start disabled (defaults to false)
26077  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
26078  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
26079    applies if enableToggle = true)
26080  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
26081  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
26082   an {@link Roo.util.ClickRepeater} config object (defaults to false).
26083  * @constructor
26084  * Create a new button
26085  * @param {Object} config The config object
26086  */
26087 Roo.Button = function(renderTo, config)
26088 {
26089     if (!config) {
26090         config = renderTo;
26091         renderTo = config.renderTo || false;
26092     }
26093     
26094     Roo.apply(this, config);
26095     this.addEvents({
26096         /**
26097              * @event click
26098              * Fires when this button is clicked
26099              * @param {Button} this
26100              * @param {EventObject} e The click event
26101              */
26102             "click" : true,
26103         /**
26104              * @event toggle
26105              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
26106              * @param {Button} this
26107              * @param {Boolean} pressed
26108              */
26109             "toggle" : true,
26110         /**
26111              * @event mouseover
26112              * Fires when the mouse hovers over the button
26113              * @param {Button} this
26114              * @param {Event} e The event object
26115              */
26116         'mouseover' : true,
26117         /**
26118              * @event mouseout
26119              * Fires when the mouse exits the button
26120              * @param {Button} this
26121              * @param {Event} e The event object
26122              */
26123         'mouseout': true,
26124          /**
26125              * @event render
26126              * Fires when the button is rendered
26127              * @param {Button} this
26128              */
26129         'render': true
26130     });
26131     if(this.menu){
26132         this.menu = Roo.menu.MenuMgr.get(this.menu);
26133     }
26134     // register listeners first!!  - so render can be captured..
26135     Roo.util.Observable.call(this);
26136     if(renderTo){
26137         this.render(renderTo);
26138     }
26139     
26140   
26141 };
26142
26143 Roo.extend(Roo.Button, Roo.util.Observable, {
26144     /**
26145      * 
26146      */
26147     
26148     /**
26149      * Read-only. True if this button is hidden
26150      * @type Boolean
26151      */
26152     hidden : false,
26153     /**
26154      * Read-only. True if this button is disabled
26155      * @type Boolean
26156      */
26157     disabled : false,
26158     /**
26159      * Read-only. True if this button is pressed (only if enableToggle = true)
26160      * @type Boolean
26161      */
26162     pressed : false,
26163
26164     /**
26165      * @cfg {Number} tabIndex 
26166      * The DOM tabIndex for this button (defaults to undefined)
26167      */
26168     tabIndex : undefined,
26169
26170     /**
26171      * @cfg {Boolean} enableToggle
26172      * True to enable pressed/not pressed toggling (defaults to false)
26173      */
26174     enableToggle: false,
26175     /**
26176      * @cfg {Mixed} menu
26177      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
26178      */
26179     menu : undefined,
26180     /**
26181      * @cfg {String} menuAlign
26182      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
26183      */
26184     menuAlign : "tl-bl?",
26185
26186     /**
26187      * @cfg {String} iconCls
26188      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
26189      */
26190     iconCls : undefined,
26191     /**
26192      * @cfg {String} type
26193      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
26194      */
26195     type : 'button',
26196
26197     // private
26198     menuClassTarget: 'tr',
26199
26200     /**
26201      * @cfg {String} clickEvent
26202      * The type of event to map to the button's event handler (defaults to 'click')
26203      */
26204     clickEvent : 'click',
26205
26206     /**
26207      * @cfg {Boolean} handleMouseEvents
26208      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
26209      */
26210     handleMouseEvents : true,
26211
26212     /**
26213      * @cfg {String} tooltipType
26214      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26215      */
26216     tooltipType : 'qtip',
26217
26218     /**
26219      * @cfg {String} cls
26220      * A CSS class to apply to the button's main element.
26221      */
26222     
26223     /**
26224      * @cfg {Roo.Template} template (Optional)
26225      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26226      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26227      * require code modifications if required elements (e.g. a button) aren't present.
26228      */
26229
26230     // private
26231     render : function(renderTo){
26232         var btn;
26233         if(this.hideParent){
26234             this.parentEl = Roo.get(renderTo);
26235         }
26236         if(!this.dhconfig){
26237             if(!this.template){
26238                 if(!Roo.Button.buttonTemplate){
26239                     // hideous table template
26240                     Roo.Button.buttonTemplate = new Roo.Template(
26241                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26242                         '<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>',
26243                         "</tr></tbody></table>");
26244                 }
26245                 this.template = Roo.Button.buttonTemplate;
26246             }
26247             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26248             var btnEl = btn.child("button:first");
26249             btnEl.on('focus', this.onFocus, this);
26250             btnEl.on('blur', this.onBlur, this);
26251             if(this.cls){
26252                 btn.addClass(this.cls);
26253             }
26254             if(this.icon){
26255                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26256             }
26257             if(this.iconCls){
26258                 btnEl.addClass(this.iconCls);
26259                 if(!this.cls){
26260                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26261                 }
26262             }
26263             if(this.tabIndex !== undefined){
26264                 btnEl.dom.tabIndex = this.tabIndex;
26265             }
26266             if(this.tooltip){
26267                 if(typeof this.tooltip == 'object'){
26268                     Roo.QuickTips.tips(Roo.apply({
26269                           target: btnEl.id
26270                     }, this.tooltip));
26271                 } else {
26272                     btnEl.dom[this.tooltipType] = this.tooltip;
26273                 }
26274             }
26275         }else{
26276             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26277         }
26278         this.el = btn;
26279         if(this.id){
26280             this.el.dom.id = this.el.id = this.id;
26281         }
26282         if(this.menu){
26283             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26284             this.menu.on("show", this.onMenuShow, this);
26285             this.menu.on("hide", this.onMenuHide, this);
26286         }
26287         btn.addClass("x-btn");
26288         if(Roo.isIE && !Roo.isIE7){
26289             this.autoWidth.defer(1, this);
26290         }else{
26291             this.autoWidth();
26292         }
26293         if(this.handleMouseEvents){
26294             btn.on("mouseover", this.onMouseOver, this);
26295             btn.on("mouseout", this.onMouseOut, this);
26296             btn.on("mousedown", this.onMouseDown, this);
26297         }
26298         btn.on(this.clickEvent, this.onClick, this);
26299         //btn.on("mouseup", this.onMouseUp, this);
26300         if(this.hidden){
26301             this.hide();
26302         }
26303         if(this.disabled){
26304             this.disable();
26305         }
26306         Roo.ButtonToggleMgr.register(this);
26307         if(this.pressed){
26308             this.el.addClass("x-btn-pressed");
26309         }
26310         if(this.repeat){
26311             var repeater = new Roo.util.ClickRepeater(btn,
26312                 typeof this.repeat == "object" ? this.repeat : {}
26313             );
26314             repeater.on("click", this.onClick,  this);
26315         }
26316         
26317         this.fireEvent('render', this);
26318         
26319     },
26320     /**
26321      * Returns the button's underlying element
26322      * @return {Roo.Element} The element
26323      */
26324     getEl : function(){
26325         return this.el;  
26326     },
26327     
26328     /**
26329      * Destroys this Button and removes any listeners.
26330      */
26331     destroy : function(){
26332         Roo.ButtonToggleMgr.unregister(this);
26333         this.el.removeAllListeners();
26334         this.purgeListeners();
26335         this.el.remove();
26336     },
26337
26338     // private
26339     autoWidth : function(){
26340         if(this.el){
26341             this.el.setWidth("auto");
26342             if(Roo.isIE7 && Roo.isStrict){
26343                 var ib = this.el.child('button');
26344                 if(ib && ib.getWidth() > 20){
26345                     ib.clip();
26346                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26347                 }
26348             }
26349             if(this.minWidth){
26350                 if(this.hidden){
26351                     this.el.beginMeasure();
26352                 }
26353                 if(this.el.getWidth() < this.minWidth){
26354                     this.el.setWidth(this.minWidth);
26355                 }
26356                 if(this.hidden){
26357                     this.el.endMeasure();
26358                 }
26359             }
26360         }
26361     },
26362
26363     /**
26364      * Assigns this button's click handler
26365      * @param {Function} handler The function to call when the button is clicked
26366      * @param {Object} scope (optional) Scope for the function passed in
26367      */
26368     setHandler : function(handler, scope){
26369         this.handler = handler;
26370         this.scope = scope;  
26371     },
26372     
26373     /**
26374      * Sets this button's text
26375      * @param {String} text The button text
26376      */
26377     setText : function(text){
26378         this.text = text;
26379         if(this.el){
26380             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26381         }
26382         this.autoWidth();
26383     },
26384     
26385     /**
26386      * Gets the text for this button
26387      * @return {String} The button text
26388      */
26389     getText : function(){
26390         return this.text;  
26391     },
26392     
26393     /**
26394      * Show this button
26395      */
26396     show: function(){
26397         this.hidden = false;
26398         if(this.el){
26399             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26400         }
26401     },
26402     
26403     /**
26404      * Hide this button
26405      */
26406     hide: function(){
26407         this.hidden = true;
26408         if(this.el){
26409             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26410         }
26411     },
26412     
26413     /**
26414      * Convenience function for boolean show/hide
26415      * @param {Boolean} visible True to show, false to hide
26416      */
26417     setVisible: function(visible){
26418         if(visible) {
26419             this.show();
26420         }else{
26421             this.hide();
26422         }
26423     },
26424     
26425     /**
26426      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26427      * @param {Boolean} state (optional) Force a particular state
26428      */
26429     toggle : function(state){
26430         state = state === undefined ? !this.pressed : state;
26431         if(state != this.pressed){
26432             if(state){
26433                 this.el.addClass("x-btn-pressed");
26434                 this.pressed = true;
26435                 this.fireEvent("toggle", this, true);
26436             }else{
26437                 this.el.removeClass("x-btn-pressed");
26438                 this.pressed = false;
26439                 this.fireEvent("toggle", this, false);
26440             }
26441             if(this.toggleHandler){
26442                 this.toggleHandler.call(this.scope || this, this, state);
26443             }
26444         }
26445     },
26446     
26447     /**
26448      * Focus the button
26449      */
26450     focus : function(){
26451         this.el.child('button:first').focus();
26452     },
26453     
26454     /**
26455      * Disable this button
26456      */
26457     disable : function(){
26458         if(this.el){
26459             this.el.addClass("x-btn-disabled");
26460         }
26461         this.disabled = true;
26462     },
26463     
26464     /**
26465      * Enable this button
26466      */
26467     enable : function(){
26468         if(this.el){
26469             this.el.removeClass("x-btn-disabled");
26470         }
26471         this.disabled = false;
26472     },
26473
26474     /**
26475      * Convenience function for boolean enable/disable
26476      * @param {Boolean} enabled True to enable, false to disable
26477      */
26478     setDisabled : function(v){
26479         this[v !== true ? "enable" : "disable"]();
26480     },
26481
26482     // private
26483     onClick : function(e){
26484         if(e){
26485             e.preventDefault();
26486         }
26487         if(e.button != 0){
26488             return;
26489         }
26490         if(!this.disabled){
26491             if(this.enableToggle){
26492                 this.toggle();
26493             }
26494             if(this.menu && !this.menu.isVisible()){
26495                 this.menu.show(this.el, this.menuAlign);
26496             }
26497             this.fireEvent("click", this, e);
26498             if(this.handler){
26499                 this.el.removeClass("x-btn-over");
26500                 this.handler.call(this.scope || this, this, e);
26501             }
26502         }
26503     },
26504     // private
26505     onMouseOver : function(e){
26506         if(!this.disabled){
26507             this.el.addClass("x-btn-over");
26508             this.fireEvent('mouseover', this, e);
26509         }
26510     },
26511     // private
26512     onMouseOut : function(e){
26513         if(!e.within(this.el,  true)){
26514             this.el.removeClass("x-btn-over");
26515             this.fireEvent('mouseout', this, e);
26516         }
26517     },
26518     // private
26519     onFocus : function(e){
26520         if(!this.disabled){
26521             this.el.addClass("x-btn-focus");
26522         }
26523     },
26524     // private
26525     onBlur : function(e){
26526         this.el.removeClass("x-btn-focus");
26527     },
26528     // private
26529     onMouseDown : function(e){
26530         if(!this.disabled && e.button == 0){
26531             this.el.addClass("x-btn-click");
26532             Roo.get(document).on('mouseup', this.onMouseUp, this);
26533         }
26534     },
26535     // private
26536     onMouseUp : function(e){
26537         if(e.button == 0){
26538             this.el.removeClass("x-btn-click");
26539             Roo.get(document).un('mouseup', this.onMouseUp, this);
26540         }
26541     },
26542     // private
26543     onMenuShow : function(e){
26544         this.el.addClass("x-btn-menu-active");
26545     },
26546     // private
26547     onMenuHide : function(e){
26548         this.el.removeClass("x-btn-menu-active");
26549     }   
26550 });
26551
26552 // Private utility class used by Button
26553 Roo.ButtonToggleMgr = function(){
26554    var groups = {};
26555    
26556    function toggleGroup(btn, state){
26557        if(state){
26558            var g = groups[btn.toggleGroup];
26559            for(var i = 0, l = g.length; i < l; i++){
26560                if(g[i] != btn){
26561                    g[i].toggle(false);
26562                }
26563            }
26564        }
26565    }
26566    
26567    return {
26568        register : function(btn){
26569            if(!btn.toggleGroup){
26570                return;
26571            }
26572            var g = groups[btn.toggleGroup];
26573            if(!g){
26574                g = groups[btn.toggleGroup] = [];
26575            }
26576            g.push(btn);
26577            btn.on("toggle", toggleGroup);
26578        },
26579        
26580        unregister : function(btn){
26581            if(!btn.toggleGroup){
26582                return;
26583            }
26584            var g = groups[btn.toggleGroup];
26585            if(g){
26586                g.remove(btn);
26587                btn.un("toggle", toggleGroup);
26588            }
26589        }
26590    };
26591 }();/*
26592  * Based on:
26593  * Ext JS Library 1.1.1
26594  * Copyright(c) 2006-2007, Ext JS, LLC.
26595  *
26596  * Originally Released Under LGPL - original licence link has changed is not relivant.
26597  *
26598  * Fork - LGPL
26599  * <script type="text/javascript">
26600  */
26601  
26602 /**
26603  * @class Roo.SplitButton
26604  * @extends Roo.Button
26605  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26606  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26607  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26608  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26609  * @cfg {String} arrowTooltip The title attribute of the arrow
26610  * @constructor
26611  * Create a new menu button
26612  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26613  * @param {Object} config The config object
26614  */
26615 Roo.SplitButton = function(renderTo, config){
26616     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26617     /**
26618      * @event arrowclick
26619      * Fires when this button's arrow is clicked
26620      * @param {SplitButton} this
26621      * @param {EventObject} e The click event
26622      */
26623     this.addEvents({"arrowclick":true});
26624 };
26625
26626 Roo.extend(Roo.SplitButton, Roo.Button, {
26627     render : function(renderTo){
26628         // this is one sweet looking template!
26629         var tpl = new Roo.Template(
26630             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26631             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26632             '<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>',
26633             "</tbody></table></td><td>",
26634             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26635             '<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>',
26636             "</tbody></table></td></tr></table>"
26637         );
26638         var btn = tpl.append(renderTo, [this.text, this.type], true);
26639         var btnEl = btn.child("button");
26640         if(this.cls){
26641             btn.addClass(this.cls);
26642         }
26643         if(this.icon){
26644             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26645         }
26646         if(this.iconCls){
26647             btnEl.addClass(this.iconCls);
26648             if(!this.cls){
26649                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26650             }
26651         }
26652         this.el = btn;
26653         if(this.handleMouseEvents){
26654             btn.on("mouseover", this.onMouseOver, this);
26655             btn.on("mouseout", this.onMouseOut, this);
26656             btn.on("mousedown", this.onMouseDown, this);
26657             btn.on("mouseup", this.onMouseUp, this);
26658         }
26659         btn.on(this.clickEvent, this.onClick, this);
26660         if(this.tooltip){
26661             if(typeof this.tooltip == 'object'){
26662                 Roo.QuickTips.tips(Roo.apply({
26663                       target: btnEl.id
26664                 }, this.tooltip));
26665             } else {
26666                 btnEl.dom[this.tooltipType] = this.tooltip;
26667             }
26668         }
26669         if(this.arrowTooltip){
26670             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26671         }
26672         if(this.hidden){
26673             this.hide();
26674         }
26675         if(this.disabled){
26676             this.disable();
26677         }
26678         if(this.pressed){
26679             this.el.addClass("x-btn-pressed");
26680         }
26681         if(Roo.isIE && !Roo.isIE7){
26682             this.autoWidth.defer(1, this);
26683         }else{
26684             this.autoWidth();
26685         }
26686         if(this.menu){
26687             this.menu.on("show", this.onMenuShow, this);
26688             this.menu.on("hide", this.onMenuHide, this);
26689         }
26690         this.fireEvent('render', this);
26691     },
26692
26693     // private
26694     autoWidth : function(){
26695         if(this.el){
26696             var tbl = this.el.child("table:first");
26697             var tbl2 = this.el.child("table:last");
26698             this.el.setWidth("auto");
26699             tbl.setWidth("auto");
26700             if(Roo.isIE7 && Roo.isStrict){
26701                 var ib = this.el.child('button:first');
26702                 if(ib && ib.getWidth() > 20){
26703                     ib.clip();
26704                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26705                 }
26706             }
26707             if(this.minWidth){
26708                 if(this.hidden){
26709                     this.el.beginMeasure();
26710                 }
26711                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26712                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26713                 }
26714                 if(this.hidden){
26715                     this.el.endMeasure();
26716                 }
26717             }
26718             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26719         } 
26720     },
26721     /**
26722      * Sets this button's click handler
26723      * @param {Function} handler The function to call when the button is clicked
26724      * @param {Object} scope (optional) Scope for the function passed above
26725      */
26726     setHandler : function(handler, scope){
26727         this.handler = handler;
26728         this.scope = scope;  
26729     },
26730     
26731     /**
26732      * Sets this button's arrow click handler
26733      * @param {Function} handler The function to call when the arrow is clicked
26734      * @param {Object} scope (optional) Scope for the function passed above
26735      */
26736     setArrowHandler : function(handler, scope){
26737         this.arrowHandler = handler;
26738         this.scope = scope;  
26739     },
26740     
26741     /**
26742      * Focus the button
26743      */
26744     focus : function(){
26745         if(this.el){
26746             this.el.child("button:first").focus();
26747         }
26748     },
26749
26750     // private
26751     onClick : function(e){
26752         e.preventDefault();
26753         if(!this.disabled){
26754             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26755                 if(this.menu && !this.menu.isVisible()){
26756                     this.menu.show(this.el, this.menuAlign);
26757                 }
26758                 this.fireEvent("arrowclick", this, e);
26759                 if(this.arrowHandler){
26760                     this.arrowHandler.call(this.scope || this, this, e);
26761                 }
26762             }else{
26763                 this.fireEvent("click", this, e);
26764                 if(this.handler){
26765                     this.handler.call(this.scope || this, this, e);
26766                 }
26767             }
26768         }
26769     },
26770     // private
26771     onMouseDown : function(e){
26772         if(!this.disabled){
26773             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26774         }
26775     },
26776     // private
26777     onMouseUp : function(e){
26778         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26779     }   
26780 });
26781
26782
26783 // backwards compat
26784 Roo.MenuButton = Roo.SplitButton;/*
26785  * Based on:
26786  * Ext JS Library 1.1.1
26787  * Copyright(c) 2006-2007, Ext JS, LLC.
26788  *
26789  * Originally Released Under LGPL - original licence link has changed is not relivant.
26790  *
26791  * Fork - LGPL
26792  * <script type="text/javascript">
26793  */
26794
26795 /**
26796  * @class Roo.Toolbar
26797  * Basic Toolbar class.
26798  * @constructor
26799  * Creates a new Toolbar
26800  * @param {Object} container The config object
26801  */ 
26802 Roo.Toolbar = function(container, buttons, config)
26803 {
26804     /// old consturctor format still supported..
26805     if(container instanceof Array){ // omit the container for later rendering
26806         buttons = container;
26807         config = buttons;
26808         container = null;
26809     }
26810     if (typeof(container) == 'object' && container.xtype) {
26811         config = container;
26812         container = config.container;
26813         buttons = config.buttons || []; // not really - use items!!
26814     }
26815     var xitems = [];
26816     if (config && config.items) {
26817         xitems = config.items;
26818         delete config.items;
26819     }
26820     Roo.apply(this, config);
26821     this.buttons = buttons;
26822     
26823     if(container){
26824         this.render(container);
26825     }
26826     this.xitems = xitems;
26827     Roo.each(xitems, function(b) {
26828         this.add(b);
26829     }, this);
26830     
26831 };
26832
26833 Roo.Toolbar.prototype = {
26834     /**
26835      * @cfg {Array} items
26836      * array of button configs or elements to add (will be converted to a MixedCollection)
26837      */
26838     
26839     /**
26840      * @cfg {String/HTMLElement/Element} container
26841      * The id or element that will contain the toolbar
26842      */
26843     // private
26844     render : function(ct){
26845         this.el = Roo.get(ct);
26846         if(this.cls){
26847             this.el.addClass(this.cls);
26848         }
26849         // using a table allows for vertical alignment
26850         // 100% width is needed by Safari...
26851         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26852         this.tr = this.el.child("tr", true);
26853         var autoId = 0;
26854         this.items = new Roo.util.MixedCollection(false, function(o){
26855             return o.id || ("item" + (++autoId));
26856         });
26857         if(this.buttons){
26858             this.add.apply(this, this.buttons);
26859             delete this.buttons;
26860         }
26861     },
26862
26863     /**
26864      * Adds element(s) to the toolbar -- this function takes a variable number of 
26865      * arguments of mixed type and adds them to the toolbar.
26866      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26867      * <ul>
26868      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26869      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26870      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26871      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26872      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26873      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26874      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26875      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26876      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26877      * </ul>
26878      * @param {Mixed} arg2
26879      * @param {Mixed} etc.
26880      */
26881     add : function(){
26882         var a = arguments, l = a.length;
26883         for(var i = 0; i < l; i++){
26884             this._add(a[i]);
26885         }
26886     },
26887     // private..
26888     _add : function(el) {
26889         
26890         if (el.xtype) {
26891             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26892         }
26893         
26894         if (el.applyTo){ // some kind of form field
26895             return this.addField(el);
26896         } 
26897         if (el.render){ // some kind of Toolbar.Item
26898             return this.addItem(el);
26899         }
26900         if (typeof el == "string"){ // string
26901             if(el == "separator" || el == "-"){
26902                 return this.addSeparator();
26903             }
26904             if (el == " "){
26905                 return this.addSpacer();
26906             }
26907             if(el == "->"){
26908                 return this.addFill();
26909             }
26910             return this.addText(el);
26911             
26912         }
26913         if(el.tagName){ // element
26914             return this.addElement(el);
26915         }
26916         if(typeof el == "object"){ // must be button config?
26917             return this.addButton(el);
26918         }
26919         // and now what?!?!
26920         return false;
26921         
26922     },
26923     
26924     /**
26925      * Add an Xtype element
26926      * @param {Object} xtype Xtype Object
26927      * @return {Object} created Object
26928      */
26929     addxtype : function(e){
26930         return this.add(e);  
26931     },
26932     
26933     /**
26934      * Returns the Element for this toolbar.
26935      * @return {Roo.Element}
26936      */
26937     getEl : function(){
26938         return this.el;  
26939     },
26940     
26941     /**
26942      * Adds a separator
26943      * @return {Roo.Toolbar.Item} The separator item
26944      */
26945     addSeparator : function(){
26946         return this.addItem(new Roo.Toolbar.Separator());
26947     },
26948
26949     /**
26950      * Adds a spacer element
26951      * @return {Roo.Toolbar.Spacer} The spacer item
26952      */
26953     addSpacer : function(){
26954         return this.addItem(new Roo.Toolbar.Spacer());
26955     },
26956
26957     /**
26958      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26959      * @return {Roo.Toolbar.Fill} The fill item
26960      */
26961     addFill : function(){
26962         return this.addItem(new Roo.Toolbar.Fill());
26963     },
26964
26965     /**
26966      * Adds any standard HTML element to the toolbar
26967      * @param {String/HTMLElement/Element} el The element or id of the element to add
26968      * @return {Roo.Toolbar.Item} The element's item
26969      */
26970     addElement : function(el){
26971         return this.addItem(new Roo.Toolbar.Item(el));
26972     },
26973     /**
26974      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26975      * @type Roo.util.MixedCollection  
26976      */
26977     items : false,
26978      
26979     /**
26980      * Adds any Toolbar.Item or subclass
26981      * @param {Roo.Toolbar.Item} item
26982      * @return {Roo.Toolbar.Item} The item
26983      */
26984     addItem : function(item){
26985         var td = this.nextBlock();
26986         item.render(td);
26987         this.items.add(item);
26988         return item;
26989     },
26990     
26991     /**
26992      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26993      * @param {Object/Array} config A button config or array of configs
26994      * @return {Roo.Toolbar.Button/Array}
26995      */
26996     addButton : function(config){
26997         if(config instanceof Array){
26998             var buttons = [];
26999             for(var i = 0, len = config.length; i < len; i++) {
27000                 buttons.push(this.addButton(config[i]));
27001             }
27002             return buttons;
27003         }
27004         var b = config;
27005         if(!(config instanceof Roo.Toolbar.Button)){
27006             b = config.split ?
27007                 new Roo.Toolbar.SplitButton(config) :
27008                 new Roo.Toolbar.Button(config);
27009         }
27010         var td = this.nextBlock();
27011         b.render(td);
27012         this.items.add(b);
27013         return b;
27014     },
27015     
27016     /**
27017      * Adds text to the toolbar
27018      * @param {String} text The text to add
27019      * @return {Roo.Toolbar.Item} The element's item
27020      */
27021     addText : function(text){
27022         return this.addItem(new Roo.Toolbar.TextItem(text));
27023     },
27024     
27025     /**
27026      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
27027      * @param {Number} index The index where the item is to be inserted
27028      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
27029      * @return {Roo.Toolbar.Button/Item}
27030      */
27031     insertButton : function(index, item){
27032         if(item instanceof Array){
27033             var buttons = [];
27034             for(var i = 0, len = item.length; i < len; i++) {
27035                buttons.push(this.insertButton(index + i, item[i]));
27036             }
27037             return buttons;
27038         }
27039         if (!(item instanceof Roo.Toolbar.Button)){
27040            item = new Roo.Toolbar.Button(item);
27041         }
27042         var td = document.createElement("td");
27043         this.tr.insertBefore(td, this.tr.childNodes[index]);
27044         item.render(td);
27045         this.items.insert(index, item);
27046         return item;
27047     },
27048     
27049     /**
27050      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
27051      * @param {Object} config
27052      * @return {Roo.Toolbar.Item} The element's item
27053      */
27054     addDom : function(config, returnEl){
27055         var td = this.nextBlock();
27056         Roo.DomHelper.overwrite(td, config);
27057         var ti = new Roo.Toolbar.Item(td.firstChild);
27058         ti.render(td);
27059         this.items.add(ti);
27060         return ti;
27061     },
27062
27063     /**
27064      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
27065      * @type Roo.util.MixedCollection  
27066      */
27067     fields : false,
27068     
27069     /**
27070      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
27071      * Note: the field should not have been rendered yet. For a field that has already been
27072      * rendered, use {@link #addElement}.
27073      * @param {Roo.form.Field} field
27074      * @return {Roo.ToolbarItem}
27075      */
27076      
27077       
27078     addField : function(field) {
27079         if (!this.fields) {
27080             var autoId = 0;
27081             this.fields = new Roo.util.MixedCollection(false, function(o){
27082                 return o.id || ("item" + (++autoId));
27083             });
27084
27085         }
27086         
27087         var td = this.nextBlock();
27088         field.render(td);
27089         var ti = new Roo.Toolbar.Item(td.firstChild);
27090         ti.render(td);
27091         this.items.add(ti);
27092         this.fields.add(field);
27093         return ti;
27094     },
27095     /**
27096      * Hide the toolbar
27097      * @method hide
27098      */
27099      
27100       
27101     hide : function()
27102     {
27103         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
27104         this.el.child('div').hide();
27105     },
27106     /**
27107      * Show the toolbar
27108      * @method show
27109      */
27110     show : function()
27111     {
27112         this.el.child('div').show();
27113     },
27114       
27115     // private
27116     nextBlock : function(){
27117         var td = document.createElement("td");
27118         this.tr.appendChild(td);
27119         return td;
27120     },
27121
27122     // private
27123     destroy : function(){
27124         if(this.items){ // rendered?
27125             Roo.destroy.apply(Roo, this.items.items);
27126         }
27127         if(this.fields){ // rendered?
27128             Roo.destroy.apply(Roo, this.fields.items);
27129         }
27130         Roo.Element.uncache(this.el, this.tr);
27131     }
27132 };
27133
27134 /**
27135  * @class Roo.Toolbar.Item
27136  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
27137  * @constructor
27138  * Creates a new Item
27139  * @param {HTMLElement} el 
27140  */
27141 Roo.Toolbar.Item = function(el){
27142     this.el = Roo.getDom(el);
27143     this.id = Roo.id(this.el);
27144     this.hidden = false;
27145 };
27146
27147 Roo.Toolbar.Item.prototype = {
27148     
27149     /**
27150      * Get this item's HTML Element
27151      * @return {HTMLElement}
27152      */
27153     getEl : function(){
27154        return this.el;  
27155     },
27156
27157     // private
27158     render : function(td){
27159         this.td = td;
27160         td.appendChild(this.el);
27161     },
27162     
27163     /**
27164      * Removes and destroys this item.
27165      */
27166     destroy : function(){
27167         this.td.parentNode.removeChild(this.td);
27168     },
27169     
27170     /**
27171      * Shows this item.
27172      */
27173     show: function(){
27174         this.hidden = false;
27175         this.td.style.display = "";
27176     },
27177     
27178     /**
27179      * Hides this item.
27180      */
27181     hide: function(){
27182         this.hidden = true;
27183         this.td.style.display = "none";
27184     },
27185     
27186     /**
27187      * Convenience function for boolean show/hide.
27188      * @param {Boolean} visible true to show/false to hide
27189      */
27190     setVisible: function(visible){
27191         if(visible) {
27192             this.show();
27193         }else{
27194             this.hide();
27195         }
27196     },
27197     
27198     /**
27199      * Try to focus this item.
27200      */
27201     focus : function(){
27202         Roo.fly(this.el).focus();
27203     },
27204     
27205     /**
27206      * Disables this item.
27207      */
27208     disable : function(){
27209         Roo.fly(this.td).addClass("x-item-disabled");
27210         this.disabled = true;
27211         this.el.disabled = true;
27212     },
27213     
27214     /**
27215      * Enables this item.
27216      */
27217     enable : function(){
27218         Roo.fly(this.td).removeClass("x-item-disabled");
27219         this.disabled = false;
27220         this.el.disabled = false;
27221     }
27222 };
27223
27224
27225 /**
27226  * @class Roo.Toolbar.Separator
27227  * @extends Roo.Toolbar.Item
27228  * A simple toolbar separator class
27229  * @constructor
27230  * Creates a new Separator
27231  */
27232 Roo.Toolbar.Separator = function(){
27233     var s = document.createElement("span");
27234     s.className = "ytb-sep";
27235     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27236 };
27237 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27238     enable:Roo.emptyFn,
27239     disable:Roo.emptyFn,
27240     focus:Roo.emptyFn
27241 });
27242
27243 /**
27244  * @class Roo.Toolbar.Spacer
27245  * @extends Roo.Toolbar.Item
27246  * A simple element that adds extra horizontal space to a toolbar.
27247  * @constructor
27248  * Creates a new Spacer
27249  */
27250 Roo.Toolbar.Spacer = function(){
27251     var s = document.createElement("div");
27252     s.className = "ytb-spacer";
27253     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27254 };
27255 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27256     enable:Roo.emptyFn,
27257     disable:Roo.emptyFn,
27258     focus:Roo.emptyFn
27259 });
27260
27261 /**
27262  * @class Roo.Toolbar.Fill
27263  * @extends Roo.Toolbar.Spacer
27264  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27265  * @constructor
27266  * Creates a new Spacer
27267  */
27268 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27269     // private
27270     render : function(td){
27271         td.style.width = '100%';
27272         Roo.Toolbar.Fill.superclass.render.call(this, td);
27273     }
27274 });
27275
27276 /**
27277  * @class Roo.Toolbar.TextItem
27278  * @extends Roo.Toolbar.Item
27279  * A simple class that renders text directly into a toolbar.
27280  * @constructor
27281  * Creates a new TextItem
27282  * @param {String} text
27283  */
27284 Roo.Toolbar.TextItem = function(text){
27285     if (typeof(text) == 'object') {
27286         text = text.text;
27287     }
27288     var s = document.createElement("span");
27289     s.className = "ytb-text";
27290     s.innerHTML = text;
27291     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27292 };
27293 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27294     enable:Roo.emptyFn,
27295     disable:Roo.emptyFn,
27296     focus:Roo.emptyFn
27297 });
27298
27299 /**
27300  * @class Roo.Toolbar.Button
27301  * @extends Roo.Button
27302  * A button that renders into a toolbar.
27303  * @constructor
27304  * Creates a new Button
27305  * @param {Object} config A standard {@link Roo.Button} config object
27306  */
27307 Roo.Toolbar.Button = function(config){
27308     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27309 };
27310 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27311     render : function(td){
27312         this.td = td;
27313         Roo.Toolbar.Button.superclass.render.call(this, td);
27314     },
27315     
27316     /**
27317      * Removes and destroys this button
27318      */
27319     destroy : function(){
27320         Roo.Toolbar.Button.superclass.destroy.call(this);
27321         this.td.parentNode.removeChild(this.td);
27322     },
27323     
27324     /**
27325      * Shows this button
27326      */
27327     show: function(){
27328         this.hidden = false;
27329         this.td.style.display = "";
27330     },
27331     
27332     /**
27333      * Hides this button
27334      */
27335     hide: function(){
27336         this.hidden = true;
27337         this.td.style.display = "none";
27338     },
27339
27340     /**
27341      * Disables this item
27342      */
27343     disable : function(){
27344         Roo.fly(this.td).addClass("x-item-disabled");
27345         this.disabled = true;
27346     },
27347
27348     /**
27349      * Enables this item
27350      */
27351     enable : function(){
27352         Roo.fly(this.td).removeClass("x-item-disabled");
27353         this.disabled = false;
27354     }
27355 });
27356 // backwards compat
27357 Roo.ToolbarButton = Roo.Toolbar.Button;
27358
27359 /**
27360  * @class Roo.Toolbar.SplitButton
27361  * @extends Roo.SplitButton
27362  * A menu button that renders into a toolbar.
27363  * @constructor
27364  * Creates a new SplitButton
27365  * @param {Object} config A standard {@link Roo.SplitButton} config object
27366  */
27367 Roo.Toolbar.SplitButton = function(config){
27368     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27369 };
27370 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27371     render : function(td){
27372         this.td = td;
27373         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27374     },
27375     
27376     /**
27377      * Removes and destroys this button
27378      */
27379     destroy : function(){
27380         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27381         this.td.parentNode.removeChild(this.td);
27382     },
27383     
27384     /**
27385      * Shows this button
27386      */
27387     show: function(){
27388         this.hidden = false;
27389         this.td.style.display = "";
27390     },
27391     
27392     /**
27393      * Hides this button
27394      */
27395     hide: function(){
27396         this.hidden = true;
27397         this.td.style.display = "none";
27398     }
27399 });
27400
27401 // backwards compat
27402 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27403  * Based on:
27404  * Ext JS Library 1.1.1
27405  * Copyright(c) 2006-2007, Ext JS, LLC.
27406  *
27407  * Originally Released Under LGPL - original licence link has changed is not relivant.
27408  *
27409  * Fork - LGPL
27410  * <script type="text/javascript">
27411  */
27412  
27413 /**
27414  * @class Roo.PagingToolbar
27415  * @extends Roo.Toolbar
27416  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27417  * @constructor
27418  * Create a new PagingToolbar
27419  * @param {Object} config The config object
27420  */
27421 Roo.PagingToolbar = function(el, ds, config)
27422 {
27423     // old args format still supported... - xtype is prefered..
27424     if (typeof(el) == 'object' && el.xtype) {
27425         // created from xtype...
27426         config = el;
27427         ds = el.dataSource;
27428         el = config.container;
27429     }
27430     var items = [];
27431     if (config.items) {
27432         items = config.items;
27433         config.items = [];
27434     }
27435     
27436     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27437     this.ds = ds;
27438     this.cursor = 0;
27439     this.renderButtons(this.el);
27440     this.bind(ds);
27441     
27442     // supprot items array.
27443    
27444     Roo.each(items, function(e) {
27445         this.add(Roo.factory(e));
27446     },this);
27447     
27448 };
27449
27450 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27451     /**
27452      * @cfg {Roo.data.Store} dataSource
27453      * The underlying data store providing the paged data
27454      */
27455     /**
27456      * @cfg {String/HTMLElement/Element} container
27457      * container The id or element that will contain the toolbar
27458      */
27459     /**
27460      * @cfg {Boolean} displayInfo
27461      * True to display the displayMsg (defaults to false)
27462      */
27463     /**
27464      * @cfg {Number} pageSize
27465      * The number of records to display per page (defaults to 20)
27466      */
27467     pageSize: 20,
27468     /**
27469      * @cfg {String} displayMsg
27470      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27471      */
27472     displayMsg : 'Displaying {0} - {1} of {2}',
27473     /**
27474      * @cfg {String} emptyMsg
27475      * The message to display when no records are found (defaults to "No data to display")
27476      */
27477     emptyMsg : 'No data to display',
27478     /**
27479      * Customizable piece of the default paging text (defaults to "Page")
27480      * @type String
27481      */
27482     beforePageText : "Page",
27483     /**
27484      * Customizable piece of the default paging text (defaults to "of %0")
27485      * @type String
27486      */
27487     afterPageText : "of {0}",
27488     /**
27489      * Customizable piece of the default paging text (defaults to "First Page")
27490      * @type String
27491      */
27492     firstText : "First Page",
27493     /**
27494      * Customizable piece of the default paging text (defaults to "Previous Page")
27495      * @type String
27496      */
27497     prevText : "Previous Page",
27498     /**
27499      * Customizable piece of the default paging text (defaults to "Next Page")
27500      * @type String
27501      */
27502     nextText : "Next Page",
27503     /**
27504      * Customizable piece of the default paging text (defaults to "Last Page")
27505      * @type String
27506      */
27507     lastText : "Last Page",
27508     /**
27509      * Customizable piece of the default paging text (defaults to "Refresh")
27510      * @type String
27511      */
27512     refreshText : "Refresh",
27513
27514     // private
27515     renderButtons : function(el){
27516         Roo.PagingToolbar.superclass.render.call(this, el);
27517         this.first = this.addButton({
27518             tooltip: this.firstText,
27519             cls: "x-btn-icon x-grid-page-first",
27520             disabled: true,
27521             handler: this.onClick.createDelegate(this, ["first"])
27522         });
27523         this.prev = this.addButton({
27524             tooltip: this.prevText,
27525             cls: "x-btn-icon x-grid-page-prev",
27526             disabled: true,
27527             handler: this.onClick.createDelegate(this, ["prev"])
27528         });
27529         //this.addSeparator();
27530         this.add(this.beforePageText);
27531         this.field = Roo.get(this.addDom({
27532            tag: "input",
27533            type: "text",
27534            size: "3",
27535            value: "1",
27536            cls: "x-grid-page-number"
27537         }).el);
27538         this.field.on("keydown", this.onPagingKeydown, this);
27539         this.field.on("focus", function(){this.dom.select();});
27540         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27541         this.field.setHeight(18);
27542         //this.addSeparator();
27543         this.next = this.addButton({
27544             tooltip: this.nextText,
27545             cls: "x-btn-icon x-grid-page-next",
27546             disabled: true,
27547             handler: this.onClick.createDelegate(this, ["next"])
27548         });
27549         this.last = this.addButton({
27550             tooltip: this.lastText,
27551             cls: "x-btn-icon x-grid-page-last",
27552             disabled: true,
27553             handler: this.onClick.createDelegate(this, ["last"])
27554         });
27555         //this.addSeparator();
27556         this.loading = this.addButton({
27557             tooltip: this.refreshText,
27558             cls: "x-btn-icon x-grid-loading",
27559             handler: this.onClick.createDelegate(this, ["refresh"])
27560         });
27561
27562         if(this.displayInfo){
27563             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27564         }
27565     },
27566
27567     // private
27568     updateInfo : function(){
27569         if(this.displayEl){
27570             var count = this.ds.getCount();
27571             var msg = count == 0 ?
27572                 this.emptyMsg :
27573                 String.format(
27574                     this.displayMsg,
27575                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27576                 );
27577             this.displayEl.update(msg);
27578         }
27579     },
27580
27581     // private
27582     onLoad : function(ds, r, o){
27583        this.cursor = o.params ? o.params.start : 0;
27584        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27585
27586        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27587        this.field.dom.value = ap;
27588        this.first.setDisabled(ap == 1);
27589        this.prev.setDisabled(ap == 1);
27590        this.next.setDisabled(ap == ps);
27591        this.last.setDisabled(ap == ps);
27592        this.loading.enable();
27593        this.updateInfo();
27594     },
27595
27596     // private
27597     getPageData : function(){
27598         var total = this.ds.getTotalCount();
27599         return {
27600             total : total,
27601             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27602             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27603         };
27604     },
27605
27606     // private
27607     onLoadError : function(){
27608         this.loading.enable();
27609     },
27610
27611     // private
27612     onPagingKeydown : function(e){
27613         var k = e.getKey();
27614         var d = this.getPageData();
27615         if(k == e.RETURN){
27616             var v = this.field.dom.value, pageNum;
27617             if(!v || isNaN(pageNum = parseInt(v, 10))){
27618                 this.field.dom.value = d.activePage;
27619                 return;
27620             }
27621             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27622             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27623             e.stopEvent();
27624         }
27625         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))
27626         {
27627           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27628           this.field.dom.value = pageNum;
27629           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27630           e.stopEvent();
27631         }
27632         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27633         {
27634           var v = this.field.dom.value, pageNum; 
27635           var increment = (e.shiftKey) ? 10 : 1;
27636           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27637             increment *= -1;
27638           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27639             this.field.dom.value = d.activePage;
27640             return;
27641           }
27642           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27643           {
27644             this.field.dom.value = parseInt(v, 10) + increment;
27645             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27646             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27647           }
27648           e.stopEvent();
27649         }
27650     },
27651
27652     // private
27653     beforeLoad : function(){
27654         if(this.loading){
27655             this.loading.disable();
27656         }
27657     },
27658
27659     // private
27660     onClick : function(which){
27661         var ds = this.ds;
27662         switch(which){
27663             case "first":
27664                 ds.load({params:{start: 0, limit: this.pageSize}});
27665             break;
27666             case "prev":
27667                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27668             break;
27669             case "next":
27670                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27671             break;
27672             case "last":
27673                 var total = ds.getTotalCount();
27674                 var extra = total % this.pageSize;
27675                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27676                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27677             break;
27678             case "refresh":
27679                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27680             break;
27681         }
27682     },
27683
27684     /**
27685      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27686      * @param {Roo.data.Store} store The data store to unbind
27687      */
27688     unbind : function(ds){
27689         ds.un("beforeload", this.beforeLoad, this);
27690         ds.un("load", this.onLoad, this);
27691         ds.un("loadexception", this.onLoadError, this);
27692         ds.un("remove", this.updateInfo, this);
27693         ds.un("add", this.updateInfo, this);
27694         this.ds = undefined;
27695     },
27696
27697     /**
27698      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27699      * @param {Roo.data.Store} store The data store to bind
27700      */
27701     bind : function(ds){
27702         ds.on("beforeload", this.beforeLoad, this);
27703         ds.on("load", this.onLoad, this);
27704         ds.on("loadexception", this.onLoadError, this);
27705         ds.on("remove", this.updateInfo, this);
27706         ds.on("add", this.updateInfo, this);
27707         this.ds = ds;
27708     }
27709 });/*
27710  * Based on:
27711  * Ext JS Library 1.1.1
27712  * Copyright(c) 2006-2007, Ext JS, LLC.
27713  *
27714  * Originally Released Under LGPL - original licence link has changed is not relivant.
27715  *
27716  * Fork - LGPL
27717  * <script type="text/javascript">
27718  */
27719
27720 /**
27721  * @class Roo.Resizable
27722  * @extends Roo.util.Observable
27723  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27724  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27725  * 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
27726  * the element will be wrapped for you automatically.</p>
27727  * <p>Here is the list of valid resize handles:</p>
27728  * <pre>
27729 Value   Description
27730 ------  -------------------
27731  'n'     north
27732  's'     south
27733  'e'     east
27734  'w'     west
27735  'nw'    northwest
27736  'sw'    southwest
27737  'se'    southeast
27738  'ne'    northeast
27739  'hd'    horizontal drag
27740  'all'   all
27741 </pre>
27742  * <p>Here's an example showing the creation of a typical Resizable:</p>
27743  * <pre><code>
27744 var resizer = new Roo.Resizable("element-id", {
27745     handles: 'all',
27746     minWidth: 200,
27747     minHeight: 100,
27748     maxWidth: 500,
27749     maxHeight: 400,
27750     pinned: true
27751 });
27752 resizer.on("resize", myHandler);
27753 </code></pre>
27754  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27755  * resizer.east.setDisplayed(false);</p>
27756  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27757  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27758  * resize operation's new size (defaults to [0, 0])
27759  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27760  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27761  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27762  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27763  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27764  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27765  * @cfg {Number} width The width of the element in pixels (defaults to null)
27766  * @cfg {Number} height The height of the element in pixels (defaults to null)
27767  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27768  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27769  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27770  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27771  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27772  * in favor of the handles config option (defaults to false)
27773  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27774  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27775  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27776  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27777  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27778  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27779  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27780  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27781  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27782  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27783  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27784  * @constructor
27785  * Create a new resizable component
27786  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27787  * @param {Object} config configuration options
27788   */
27789 Roo.Resizable = function(el, config)
27790 {
27791     this.el = Roo.get(el);
27792
27793     if(config && config.wrap){
27794         config.resizeChild = this.el;
27795         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27796         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27797         this.el.setStyle("overflow", "hidden");
27798         this.el.setPositioning(config.resizeChild.getPositioning());
27799         config.resizeChild.clearPositioning();
27800         if(!config.width || !config.height){
27801             var csize = config.resizeChild.getSize();
27802             this.el.setSize(csize.width, csize.height);
27803         }
27804         if(config.pinned && !config.adjustments){
27805             config.adjustments = "auto";
27806         }
27807     }
27808
27809     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27810     this.proxy.unselectable();
27811     this.proxy.enableDisplayMode('block');
27812
27813     Roo.apply(this, config);
27814
27815     if(this.pinned){
27816         this.disableTrackOver = true;
27817         this.el.addClass("x-resizable-pinned");
27818     }
27819     // if the element isn't positioned, make it relative
27820     var position = this.el.getStyle("position");
27821     if(position != "absolute" && position != "fixed"){
27822         this.el.setStyle("position", "relative");
27823     }
27824     if(!this.handles){ // no handles passed, must be legacy style
27825         this.handles = 's,e,se';
27826         if(this.multiDirectional){
27827             this.handles += ',n,w';
27828         }
27829     }
27830     if(this.handles == "all"){
27831         this.handles = "n s e w ne nw se sw";
27832     }
27833     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27834     var ps = Roo.Resizable.positions;
27835     for(var i = 0, len = hs.length; i < len; i++){
27836         if(hs[i] && ps[hs[i]]){
27837             var pos = ps[hs[i]];
27838             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27839         }
27840     }
27841     // legacy
27842     this.corner = this.southeast;
27843     
27844     // updateBox = the box can move..
27845     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27846         this.updateBox = true;
27847     }
27848
27849     this.activeHandle = null;
27850
27851     if(this.resizeChild){
27852         if(typeof this.resizeChild == "boolean"){
27853             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27854         }else{
27855             this.resizeChild = Roo.get(this.resizeChild, true);
27856         }
27857     }
27858     
27859     if(this.adjustments == "auto"){
27860         var rc = this.resizeChild;
27861         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27862         if(rc && (hw || hn)){
27863             rc.position("relative");
27864             rc.setLeft(hw ? hw.el.getWidth() : 0);
27865             rc.setTop(hn ? hn.el.getHeight() : 0);
27866         }
27867         this.adjustments = [
27868             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27869             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27870         ];
27871     }
27872
27873     if(this.draggable){
27874         this.dd = this.dynamic ?
27875             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27876         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27877     }
27878
27879     // public events
27880     this.addEvents({
27881         /**
27882          * @event beforeresize
27883          * Fired before resize is allowed. Set enabled to false to cancel resize.
27884          * @param {Roo.Resizable} this
27885          * @param {Roo.EventObject} e The mousedown event
27886          */
27887         "beforeresize" : true,
27888         /**
27889          * @event resize
27890          * Fired after a resize.
27891          * @param {Roo.Resizable} this
27892          * @param {Number} width The new width
27893          * @param {Number} height The new height
27894          * @param {Roo.EventObject} e The mouseup event
27895          */
27896         "resize" : true
27897     });
27898
27899     if(this.width !== null && this.height !== null){
27900         this.resizeTo(this.width, this.height);
27901     }else{
27902         this.updateChildSize();
27903     }
27904     if(Roo.isIE){
27905         this.el.dom.style.zoom = 1;
27906     }
27907     Roo.Resizable.superclass.constructor.call(this);
27908 };
27909
27910 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27911         resizeChild : false,
27912         adjustments : [0, 0],
27913         minWidth : 5,
27914         minHeight : 5,
27915         maxWidth : 10000,
27916         maxHeight : 10000,
27917         enabled : true,
27918         animate : false,
27919         duration : .35,
27920         dynamic : false,
27921         handles : false,
27922         multiDirectional : false,
27923         disableTrackOver : false,
27924         easing : 'easeOutStrong',
27925         widthIncrement : 0,
27926         heightIncrement : 0,
27927         pinned : false,
27928         width : null,
27929         height : null,
27930         preserveRatio : false,
27931         transparent: false,
27932         minX: 0,
27933         minY: 0,
27934         draggable: false,
27935
27936         /**
27937          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27938          */
27939         constrainTo: undefined,
27940         /**
27941          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27942          */
27943         resizeRegion: undefined,
27944
27945
27946     /**
27947      * Perform a manual resize
27948      * @param {Number} width
27949      * @param {Number} height
27950      */
27951     resizeTo : function(width, height){
27952         this.el.setSize(width, height);
27953         this.updateChildSize();
27954         this.fireEvent("resize", this, width, height, null);
27955     },
27956
27957     // private
27958     startSizing : function(e, handle){
27959         this.fireEvent("beforeresize", this, e);
27960         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27961
27962             if(!this.overlay){
27963                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27964                 this.overlay.unselectable();
27965                 this.overlay.enableDisplayMode("block");
27966                 this.overlay.on("mousemove", this.onMouseMove, this);
27967                 this.overlay.on("mouseup", this.onMouseUp, this);
27968             }
27969             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27970
27971             this.resizing = true;
27972             this.startBox = this.el.getBox();
27973             this.startPoint = e.getXY();
27974             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27975                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27976
27977             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27978             this.overlay.show();
27979
27980             if(this.constrainTo) {
27981                 var ct = Roo.get(this.constrainTo);
27982                 this.resizeRegion = ct.getRegion().adjust(
27983                     ct.getFrameWidth('t'),
27984                     ct.getFrameWidth('l'),
27985                     -ct.getFrameWidth('b'),
27986                     -ct.getFrameWidth('r')
27987                 );
27988             }
27989
27990             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27991             this.proxy.show();
27992             this.proxy.setBox(this.startBox);
27993             if(!this.dynamic){
27994                 this.proxy.setStyle('visibility', 'visible');
27995             }
27996         }
27997     },
27998
27999     // private
28000     onMouseDown : function(handle, e){
28001         if(this.enabled){
28002             e.stopEvent();
28003             this.activeHandle = handle;
28004             this.startSizing(e, handle);
28005         }
28006     },
28007
28008     // private
28009     onMouseUp : function(e){
28010         var size = this.resizeElement();
28011         this.resizing = false;
28012         this.handleOut();
28013         this.overlay.hide();
28014         this.proxy.hide();
28015         this.fireEvent("resize", this, size.width, size.height, e);
28016     },
28017
28018     // private
28019     updateChildSize : function(){
28020         if(this.resizeChild){
28021             var el = this.el;
28022             var child = this.resizeChild;
28023             var adj = this.adjustments;
28024             if(el.dom.offsetWidth){
28025                 var b = el.getSize(true);
28026                 child.setSize(b.width+adj[0], b.height+adj[1]);
28027             }
28028             // Second call here for IE
28029             // The first call enables instant resizing and
28030             // the second call corrects scroll bars if they
28031             // exist
28032             if(Roo.isIE){
28033                 setTimeout(function(){
28034                     if(el.dom.offsetWidth){
28035                         var b = el.getSize(true);
28036                         child.setSize(b.width+adj[0], b.height+adj[1]);
28037                     }
28038                 }, 10);
28039             }
28040         }
28041     },
28042
28043     // private
28044     snap : function(value, inc, min){
28045         if(!inc || !value) return value;
28046         var newValue = value;
28047         var m = value % inc;
28048         if(m > 0){
28049             if(m > (inc/2)){
28050                 newValue = value + (inc-m);
28051             }else{
28052                 newValue = value - m;
28053             }
28054         }
28055         return Math.max(min, newValue);
28056     },
28057
28058     // private
28059     resizeElement : function(){
28060         var box = this.proxy.getBox();
28061         if(this.updateBox){
28062             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
28063         }else{
28064             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
28065         }
28066         this.updateChildSize();
28067         if(!this.dynamic){
28068             this.proxy.hide();
28069         }
28070         return box;
28071     },
28072
28073     // private
28074     constrain : function(v, diff, m, mx){
28075         if(v - diff < m){
28076             diff = v - m;
28077         }else if(v - diff > mx){
28078             diff = mx - v;
28079         }
28080         return diff;
28081     },
28082
28083     // private
28084     onMouseMove : function(e){
28085         if(this.enabled){
28086             try{// try catch so if something goes wrong the user doesn't get hung
28087
28088             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
28089                 return;
28090             }
28091
28092             //var curXY = this.startPoint;
28093             var curSize = this.curSize || this.startBox;
28094             var x = this.startBox.x, y = this.startBox.y;
28095             var ox = x, oy = y;
28096             var w = curSize.width, h = curSize.height;
28097             var ow = w, oh = h;
28098             var mw = this.minWidth, mh = this.minHeight;
28099             var mxw = this.maxWidth, mxh = this.maxHeight;
28100             var wi = this.widthIncrement;
28101             var hi = this.heightIncrement;
28102
28103             var eventXY = e.getXY();
28104             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
28105             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
28106
28107             var pos = this.activeHandle.position;
28108
28109             switch(pos){
28110                 case "east":
28111                     w += diffX;
28112                     w = Math.min(Math.max(mw, w), mxw);
28113                     break;
28114              
28115                 case "south":
28116                     h += diffY;
28117                     h = Math.min(Math.max(mh, h), mxh);
28118                     break;
28119                 case "southeast":
28120                     w += diffX;
28121                     h += diffY;
28122                     w = Math.min(Math.max(mw, w), mxw);
28123                     h = Math.min(Math.max(mh, h), mxh);
28124                     break;
28125                 case "north":
28126                     diffY = this.constrain(h, diffY, mh, mxh);
28127                     y += diffY;
28128                     h -= diffY;
28129                     break;
28130                 case "hdrag":
28131                     
28132                     if (wi) {
28133                         var adiffX = Math.abs(diffX);
28134                         var sub = (adiffX % wi); // how much 
28135                         if (sub > (wi/2)) { // far enough to snap
28136                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
28137                         } else {
28138                             // remove difference.. 
28139                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
28140                         }
28141                     }
28142                     x += diffX;
28143                     x = Math.max(this.minX, x);
28144                     break;
28145                 case "west":
28146                     diffX = this.constrain(w, diffX, mw, mxw);
28147                     x += diffX;
28148                     w -= diffX;
28149                     break;
28150                 case "northeast":
28151                     w += diffX;
28152                     w = Math.min(Math.max(mw, w), mxw);
28153                     diffY = this.constrain(h, diffY, mh, mxh);
28154                     y += diffY;
28155                     h -= diffY;
28156                     break;
28157                 case "northwest":
28158                     diffX = this.constrain(w, diffX, mw, mxw);
28159                     diffY = this.constrain(h, diffY, mh, mxh);
28160                     y += diffY;
28161                     h -= diffY;
28162                     x += diffX;
28163                     w -= diffX;
28164                     break;
28165                case "southwest":
28166                     diffX = this.constrain(w, diffX, mw, mxw);
28167                     h += diffY;
28168                     h = Math.min(Math.max(mh, h), mxh);
28169                     x += diffX;
28170                     w -= diffX;
28171                     break;
28172             }
28173
28174             var sw = this.snap(w, wi, mw);
28175             var sh = this.snap(h, hi, mh);
28176             if(sw != w || sh != h){
28177                 switch(pos){
28178                     case "northeast":
28179                         y -= sh - h;
28180                     break;
28181                     case "north":
28182                         y -= sh - h;
28183                         break;
28184                     case "southwest":
28185                         x -= sw - w;
28186                     break;
28187                     case "west":
28188                         x -= sw - w;
28189                         break;
28190                     case "northwest":
28191                         x -= sw - w;
28192                         y -= sh - h;
28193                     break;
28194                 }
28195                 w = sw;
28196                 h = sh;
28197             }
28198
28199             if(this.preserveRatio){
28200                 switch(pos){
28201                     case "southeast":
28202                     case "east":
28203                         h = oh * (w/ow);
28204                         h = Math.min(Math.max(mh, h), mxh);
28205                         w = ow * (h/oh);
28206                        break;
28207                     case "south":
28208                         w = ow * (h/oh);
28209                         w = Math.min(Math.max(mw, w), mxw);
28210                         h = oh * (w/ow);
28211                         break;
28212                     case "northeast":
28213                         w = ow * (h/oh);
28214                         w = Math.min(Math.max(mw, w), mxw);
28215                         h = oh * (w/ow);
28216                     break;
28217                     case "north":
28218                         var tw = w;
28219                         w = ow * (h/oh);
28220                         w = Math.min(Math.max(mw, w), mxw);
28221                         h = oh * (w/ow);
28222                         x += (tw - w) / 2;
28223                         break;
28224                     case "southwest":
28225                         h = oh * (w/ow);
28226                         h = Math.min(Math.max(mh, h), mxh);
28227                         var tw = w;
28228                         w = ow * (h/oh);
28229                         x += tw - w;
28230                         break;
28231                     case "west":
28232                         var th = h;
28233                         h = oh * (w/ow);
28234                         h = Math.min(Math.max(mh, h), mxh);
28235                         y += (th - h) / 2;
28236                         var tw = w;
28237                         w = ow * (h/oh);
28238                         x += tw - w;
28239                        break;
28240                     case "northwest":
28241                         var tw = w;
28242                         var th = h;
28243                         h = oh * (w/ow);
28244                         h = Math.min(Math.max(mh, h), mxh);
28245                         w = ow * (h/oh);
28246                         y += th - h;
28247                         x += tw - w;
28248                        break;
28249
28250                 }
28251             }
28252             if (pos == 'hdrag') {
28253                 w = ow;
28254             }
28255             this.proxy.setBounds(x, y, w, h);
28256             if(this.dynamic){
28257                 this.resizeElement();
28258             }
28259             }catch(e){}
28260         }
28261     },
28262
28263     // private
28264     handleOver : function(){
28265         if(this.enabled){
28266             this.el.addClass("x-resizable-over");
28267         }
28268     },
28269
28270     // private
28271     handleOut : function(){
28272         if(!this.resizing){
28273             this.el.removeClass("x-resizable-over");
28274         }
28275     },
28276
28277     /**
28278      * Returns the element this component is bound to.
28279      * @return {Roo.Element}
28280      */
28281     getEl : function(){
28282         return this.el;
28283     },
28284
28285     /**
28286      * Returns the resizeChild element (or null).
28287      * @return {Roo.Element}
28288      */
28289     getResizeChild : function(){
28290         return this.resizeChild;
28291     },
28292
28293     /**
28294      * Destroys this resizable. If the element was wrapped and
28295      * removeEl is not true then the element remains.
28296      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28297      */
28298     destroy : function(removeEl){
28299         this.proxy.remove();
28300         if(this.overlay){
28301             this.overlay.removeAllListeners();
28302             this.overlay.remove();
28303         }
28304         var ps = Roo.Resizable.positions;
28305         for(var k in ps){
28306             if(typeof ps[k] != "function" && this[ps[k]]){
28307                 var h = this[ps[k]];
28308                 h.el.removeAllListeners();
28309                 h.el.remove();
28310             }
28311         }
28312         if(removeEl){
28313             this.el.update("");
28314             this.el.remove();
28315         }
28316     }
28317 });
28318
28319 // private
28320 // hash to map config positions to true positions
28321 Roo.Resizable.positions = {
28322     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28323     hd: "hdrag"
28324 };
28325
28326 // private
28327 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28328     if(!this.tpl){
28329         // only initialize the template if resizable is used
28330         var tpl = Roo.DomHelper.createTemplate(
28331             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28332         );
28333         tpl.compile();
28334         Roo.Resizable.Handle.prototype.tpl = tpl;
28335     }
28336     this.position = pos;
28337     this.rz = rz;
28338     // show north drag fro topdra
28339     var handlepos = pos == 'hdrag' ? 'north' : pos;
28340     
28341     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28342     if (pos == 'hdrag') {
28343         this.el.setStyle('cursor', 'pointer');
28344     }
28345     this.el.unselectable();
28346     if(transparent){
28347         this.el.setOpacity(0);
28348     }
28349     this.el.on("mousedown", this.onMouseDown, this);
28350     if(!disableTrackOver){
28351         this.el.on("mouseover", this.onMouseOver, this);
28352         this.el.on("mouseout", this.onMouseOut, this);
28353     }
28354 };
28355
28356 // private
28357 Roo.Resizable.Handle.prototype = {
28358     afterResize : function(rz){
28359         // do nothing
28360     },
28361     // private
28362     onMouseDown : function(e){
28363         this.rz.onMouseDown(this, e);
28364     },
28365     // private
28366     onMouseOver : function(e){
28367         this.rz.handleOver(this, e);
28368     },
28369     // private
28370     onMouseOut : function(e){
28371         this.rz.handleOut(this, e);
28372     }
28373 };/*
28374  * Based on:
28375  * Ext JS Library 1.1.1
28376  * Copyright(c) 2006-2007, Ext JS, LLC.
28377  *
28378  * Originally Released Under LGPL - original licence link has changed is not relivant.
28379  *
28380  * Fork - LGPL
28381  * <script type="text/javascript">
28382  */
28383
28384 /**
28385  * @class Roo.Editor
28386  * @extends Roo.Component
28387  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28388  * @constructor
28389  * Create a new Editor
28390  * @param {Roo.form.Field} field The Field object (or descendant)
28391  * @param {Object} config The config object
28392  */
28393 Roo.Editor = function(field, config){
28394     Roo.Editor.superclass.constructor.call(this, config);
28395     this.field = field;
28396     this.addEvents({
28397         /**
28398              * @event beforestartedit
28399              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28400              * false from the handler of this event.
28401              * @param {Editor} this
28402              * @param {Roo.Element} boundEl The underlying element bound to this editor
28403              * @param {Mixed} value The field value being set
28404              */
28405         "beforestartedit" : true,
28406         /**
28407              * @event startedit
28408              * Fires when this editor is displayed
28409              * @param {Roo.Element} boundEl The underlying element bound to this editor
28410              * @param {Mixed} value The starting field value
28411              */
28412         "startedit" : true,
28413         /**
28414              * @event beforecomplete
28415              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28416              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28417              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28418              * event will not fire since no edit actually occurred.
28419              * @param {Editor} this
28420              * @param {Mixed} value The current field value
28421              * @param {Mixed} startValue The original field value
28422              */
28423         "beforecomplete" : true,
28424         /**
28425              * @event complete
28426              * Fires after editing is complete and any changed value has been written to the underlying field.
28427              * @param {Editor} this
28428              * @param {Mixed} value The current field value
28429              * @param {Mixed} startValue The original field value
28430              */
28431         "complete" : true,
28432         /**
28433          * @event specialkey
28434          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28435          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28436          * @param {Roo.form.Field} this
28437          * @param {Roo.EventObject} e The event object
28438          */
28439         "specialkey" : true
28440     });
28441 };
28442
28443 Roo.extend(Roo.Editor, Roo.Component, {
28444     /**
28445      * @cfg {Boolean/String} autosize
28446      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28447      * or "height" to adopt the height only (defaults to false)
28448      */
28449     /**
28450      * @cfg {Boolean} revertInvalid
28451      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28452      * validation fails (defaults to true)
28453      */
28454     /**
28455      * @cfg {Boolean} ignoreNoChange
28456      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28457      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28458      * will never be ignored.
28459      */
28460     /**
28461      * @cfg {Boolean} hideEl
28462      * False to keep the bound element visible while the editor is displayed (defaults to true)
28463      */
28464     /**
28465      * @cfg {Mixed} value
28466      * The data value of the underlying field (defaults to "")
28467      */
28468     value : "",
28469     /**
28470      * @cfg {String} alignment
28471      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28472      */
28473     alignment: "c-c?",
28474     /**
28475      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28476      * for bottom-right shadow (defaults to "frame")
28477      */
28478     shadow : "frame",
28479     /**
28480      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28481      */
28482     constrain : false,
28483     /**
28484      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28485      */
28486     completeOnEnter : false,
28487     /**
28488      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28489      */
28490     cancelOnEsc : false,
28491     /**
28492      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28493      */
28494     updateEl : false,
28495
28496     // private
28497     onRender : function(ct, position){
28498         this.el = new Roo.Layer({
28499             shadow: this.shadow,
28500             cls: "x-editor",
28501             parentEl : ct,
28502             shim : this.shim,
28503             shadowOffset:4,
28504             id: this.id,
28505             constrain: this.constrain
28506         });
28507         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28508         if(this.field.msgTarget != 'title'){
28509             this.field.msgTarget = 'qtip';
28510         }
28511         this.field.render(this.el);
28512         if(Roo.isGecko){
28513             this.field.el.dom.setAttribute('autocomplete', 'off');
28514         }
28515         this.field.on("specialkey", this.onSpecialKey, this);
28516         if(this.swallowKeys){
28517             this.field.el.swallowEvent(['keydown','keypress']);
28518         }
28519         this.field.show();
28520         this.field.on("blur", this.onBlur, this);
28521         if(this.field.grow){
28522             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28523         }
28524     },
28525
28526     onSpecialKey : function(field, e)
28527     {
28528         //Roo.log('editor onSpecialKey');
28529         if(this.completeOnEnter && e.getKey() == e.ENTER){
28530             e.stopEvent();
28531             this.completeEdit();
28532             return;
28533         }
28534         // do not fire special key otherwise it might hide close the editor...
28535         if(e.getKey() == e.ENTER){    
28536             return;
28537         }
28538         if(this.cancelOnEsc && e.getKey() == e.ESC){
28539             this.cancelEdit();
28540             return;
28541         } 
28542         this.fireEvent('specialkey', field, e);
28543     
28544     },
28545
28546     /**
28547      * Starts the editing process and shows the editor.
28548      * @param {String/HTMLElement/Element} el The element to edit
28549      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28550       * to the innerHTML of el.
28551      */
28552     startEdit : function(el, value){
28553         if(this.editing){
28554             this.completeEdit();
28555         }
28556         this.boundEl = Roo.get(el);
28557         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28558         if(!this.rendered){
28559             this.render(this.parentEl || document.body);
28560         }
28561         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28562             return;
28563         }
28564         this.startValue = v;
28565         this.field.setValue(v);
28566         if(this.autoSize){
28567             var sz = this.boundEl.getSize();
28568             switch(this.autoSize){
28569                 case "width":
28570                 this.setSize(sz.width,  "");
28571                 break;
28572                 case "height":
28573                 this.setSize("",  sz.height);
28574                 break;
28575                 default:
28576                 this.setSize(sz.width,  sz.height);
28577             }
28578         }
28579         this.el.alignTo(this.boundEl, this.alignment);
28580         this.editing = true;
28581         if(Roo.QuickTips){
28582             Roo.QuickTips.disable();
28583         }
28584         this.show();
28585     },
28586
28587     /**
28588      * Sets the height and width of this editor.
28589      * @param {Number} width The new width
28590      * @param {Number} height The new height
28591      */
28592     setSize : function(w, h){
28593         this.field.setSize(w, h);
28594         if(this.el){
28595             this.el.sync();
28596         }
28597     },
28598
28599     /**
28600      * Realigns the editor to the bound field based on the current alignment config value.
28601      */
28602     realign : function(){
28603         this.el.alignTo(this.boundEl, this.alignment);
28604     },
28605
28606     /**
28607      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28608      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28609      */
28610     completeEdit : function(remainVisible){
28611         if(!this.editing){
28612             return;
28613         }
28614         var v = this.getValue();
28615         if(this.revertInvalid !== false && !this.field.isValid()){
28616             v = this.startValue;
28617             this.cancelEdit(true);
28618         }
28619         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28620             this.editing = false;
28621             this.hide();
28622             return;
28623         }
28624         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28625             this.editing = false;
28626             if(this.updateEl && this.boundEl){
28627                 this.boundEl.update(v);
28628             }
28629             if(remainVisible !== true){
28630                 this.hide();
28631             }
28632             this.fireEvent("complete", this, v, this.startValue);
28633         }
28634     },
28635
28636     // private
28637     onShow : function(){
28638         this.el.show();
28639         if(this.hideEl !== false){
28640             this.boundEl.hide();
28641         }
28642         this.field.show();
28643         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28644             this.fixIEFocus = true;
28645             this.deferredFocus.defer(50, this);
28646         }else{
28647             this.field.focus();
28648         }
28649         this.fireEvent("startedit", this.boundEl, this.startValue);
28650     },
28651
28652     deferredFocus : function(){
28653         if(this.editing){
28654             this.field.focus();
28655         }
28656     },
28657
28658     /**
28659      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28660      * reverted to the original starting value.
28661      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28662      * cancel (defaults to false)
28663      */
28664     cancelEdit : function(remainVisible){
28665         if(this.editing){
28666             this.setValue(this.startValue);
28667             if(remainVisible !== true){
28668                 this.hide();
28669             }
28670         }
28671     },
28672
28673     // private
28674     onBlur : function(){
28675         if(this.allowBlur !== true && this.editing){
28676             this.completeEdit();
28677         }
28678     },
28679
28680     // private
28681     onHide : function(){
28682         if(this.editing){
28683             this.completeEdit();
28684             return;
28685         }
28686         this.field.blur();
28687         if(this.field.collapse){
28688             this.field.collapse();
28689         }
28690         this.el.hide();
28691         if(this.hideEl !== false){
28692             this.boundEl.show();
28693         }
28694         if(Roo.QuickTips){
28695             Roo.QuickTips.enable();
28696         }
28697     },
28698
28699     /**
28700      * Sets the data value of the editor
28701      * @param {Mixed} value Any valid value supported by the underlying field
28702      */
28703     setValue : function(v){
28704         this.field.setValue(v);
28705     },
28706
28707     /**
28708      * Gets the data value of the editor
28709      * @return {Mixed} The data value
28710      */
28711     getValue : function(){
28712         return this.field.getValue();
28713     }
28714 });/*
28715  * Based on:
28716  * Ext JS Library 1.1.1
28717  * Copyright(c) 2006-2007, Ext JS, LLC.
28718  *
28719  * Originally Released Under LGPL - original licence link has changed is not relivant.
28720  *
28721  * Fork - LGPL
28722  * <script type="text/javascript">
28723  */
28724  
28725 /**
28726  * @class Roo.BasicDialog
28727  * @extends Roo.util.Observable
28728  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28729  * <pre><code>
28730 var dlg = new Roo.BasicDialog("my-dlg", {
28731     height: 200,
28732     width: 300,
28733     minHeight: 100,
28734     minWidth: 150,
28735     modal: true,
28736     proxyDrag: true,
28737     shadow: true
28738 });
28739 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28740 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28741 dlg.addButton('Cancel', dlg.hide, dlg);
28742 dlg.show();
28743 </code></pre>
28744   <b>A Dialog should always be a direct child of the body element.</b>
28745  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28746  * @cfg {String} title Default text to display in the title bar (defaults to null)
28747  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28748  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28749  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28750  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28751  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28752  * (defaults to null with no animation)
28753  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28754  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28755  * property for valid values (defaults to 'all')
28756  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28757  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28758  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28759  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28760  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28761  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28762  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28763  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28764  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28765  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28766  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28767  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28768  * draggable = true (defaults to false)
28769  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28770  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28771  * shadow (defaults to false)
28772  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28773  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28774  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28775  * @cfg {Array} buttons Array of buttons
28776  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28777  * @constructor
28778  * Create a new BasicDialog.
28779  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28780  * @param {Object} config Configuration options
28781  */
28782 Roo.BasicDialog = function(el, config){
28783     this.el = Roo.get(el);
28784     var dh = Roo.DomHelper;
28785     if(!this.el && config && config.autoCreate){
28786         if(typeof config.autoCreate == "object"){
28787             if(!config.autoCreate.id){
28788                 config.autoCreate.id = el;
28789             }
28790             this.el = dh.append(document.body,
28791                         config.autoCreate, true);
28792         }else{
28793             this.el = dh.append(document.body,
28794                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28795         }
28796     }
28797     el = this.el;
28798     el.setDisplayed(true);
28799     el.hide = this.hideAction;
28800     this.id = el.id;
28801     el.addClass("x-dlg");
28802
28803     Roo.apply(this, config);
28804
28805     this.proxy = el.createProxy("x-dlg-proxy");
28806     this.proxy.hide = this.hideAction;
28807     this.proxy.setOpacity(.5);
28808     this.proxy.hide();
28809
28810     if(config.width){
28811         el.setWidth(config.width);
28812     }
28813     if(config.height){
28814         el.setHeight(config.height);
28815     }
28816     this.size = el.getSize();
28817     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28818         this.xy = [config.x,config.y];
28819     }else{
28820         this.xy = el.getCenterXY(true);
28821     }
28822     /** The header element @type Roo.Element */
28823     this.header = el.child("> .x-dlg-hd");
28824     /** The body element @type Roo.Element */
28825     this.body = el.child("> .x-dlg-bd");
28826     /** The footer element @type Roo.Element */
28827     this.footer = el.child("> .x-dlg-ft");
28828
28829     if(!this.header){
28830         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28831     }
28832     if(!this.body){
28833         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28834     }
28835
28836     this.header.unselectable();
28837     if(this.title){
28838         this.header.update(this.title);
28839     }
28840     // this element allows the dialog to be focused for keyboard event
28841     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28842     this.focusEl.swallowEvent("click", true);
28843
28844     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28845
28846     // wrap the body and footer for special rendering
28847     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28848     if(this.footer){
28849         this.bwrap.dom.appendChild(this.footer.dom);
28850     }
28851
28852     this.bg = this.el.createChild({
28853         tag: "div", cls:"x-dlg-bg",
28854         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28855     });
28856     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28857
28858
28859     if(this.autoScroll !== false && !this.autoTabs){
28860         this.body.setStyle("overflow", "auto");
28861     }
28862
28863     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28864
28865     if(this.closable !== false){
28866         this.el.addClass("x-dlg-closable");
28867         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28868         this.close.on("click", this.closeClick, this);
28869         this.close.addClassOnOver("x-dlg-close-over");
28870     }
28871     if(this.collapsible !== false){
28872         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28873         this.collapseBtn.on("click", this.collapseClick, this);
28874         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28875         this.header.on("dblclick", this.collapseClick, this);
28876     }
28877     if(this.resizable !== false){
28878         this.el.addClass("x-dlg-resizable");
28879         this.resizer = new Roo.Resizable(el, {
28880             minWidth: this.minWidth || 80,
28881             minHeight:this.minHeight || 80,
28882             handles: this.resizeHandles || "all",
28883             pinned: true
28884         });
28885         this.resizer.on("beforeresize", this.beforeResize, this);
28886         this.resizer.on("resize", this.onResize, this);
28887     }
28888     if(this.draggable !== false){
28889         el.addClass("x-dlg-draggable");
28890         if (!this.proxyDrag) {
28891             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28892         }
28893         else {
28894             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28895         }
28896         dd.setHandleElId(this.header.id);
28897         dd.endDrag = this.endMove.createDelegate(this);
28898         dd.startDrag = this.startMove.createDelegate(this);
28899         dd.onDrag = this.onDrag.createDelegate(this);
28900         dd.scroll = false;
28901         this.dd = dd;
28902     }
28903     if(this.modal){
28904         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28905         this.mask.enableDisplayMode("block");
28906         this.mask.hide();
28907         this.el.addClass("x-dlg-modal");
28908     }
28909     if(this.shadow){
28910         this.shadow = new Roo.Shadow({
28911             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28912             offset : this.shadowOffset
28913         });
28914     }else{
28915         this.shadowOffset = 0;
28916     }
28917     if(Roo.useShims && this.shim !== false){
28918         this.shim = this.el.createShim();
28919         this.shim.hide = this.hideAction;
28920         this.shim.hide();
28921     }else{
28922         this.shim = false;
28923     }
28924     if(this.autoTabs){
28925         this.initTabs();
28926     }
28927     if (this.buttons) { 
28928         var bts= this.buttons;
28929         this.buttons = [];
28930         Roo.each(bts, function(b) {
28931             this.addButton(b);
28932         }, this);
28933     }
28934     
28935     
28936     this.addEvents({
28937         /**
28938          * @event keydown
28939          * Fires when a key is pressed
28940          * @param {Roo.BasicDialog} this
28941          * @param {Roo.EventObject} e
28942          */
28943         "keydown" : true,
28944         /**
28945          * @event move
28946          * Fires when this dialog is moved by the user.
28947          * @param {Roo.BasicDialog} this
28948          * @param {Number} x The new page X
28949          * @param {Number} y The new page Y
28950          */
28951         "move" : true,
28952         /**
28953          * @event resize
28954          * Fires when this dialog is resized by the user.
28955          * @param {Roo.BasicDialog} this
28956          * @param {Number} width The new width
28957          * @param {Number} height The new height
28958          */
28959         "resize" : true,
28960         /**
28961          * @event beforehide
28962          * Fires before this dialog is hidden.
28963          * @param {Roo.BasicDialog} this
28964          */
28965         "beforehide" : true,
28966         /**
28967          * @event hide
28968          * Fires when this dialog is hidden.
28969          * @param {Roo.BasicDialog} this
28970          */
28971         "hide" : true,
28972         /**
28973          * @event beforeshow
28974          * Fires before this dialog is shown.
28975          * @param {Roo.BasicDialog} this
28976          */
28977         "beforeshow" : true,
28978         /**
28979          * @event show
28980          * Fires when this dialog is shown.
28981          * @param {Roo.BasicDialog} this
28982          */
28983         "show" : true
28984     });
28985     el.on("keydown", this.onKeyDown, this);
28986     el.on("mousedown", this.toFront, this);
28987     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28988     this.el.hide();
28989     Roo.DialogManager.register(this);
28990     Roo.BasicDialog.superclass.constructor.call(this);
28991 };
28992
28993 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28994     shadowOffset: Roo.isIE ? 6 : 5,
28995     minHeight: 80,
28996     minWidth: 200,
28997     minButtonWidth: 75,
28998     defaultButton: null,
28999     buttonAlign: "right",
29000     tabTag: 'div',
29001     firstShow: true,
29002
29003     /**
29004      * Sets the dialog title text
29005      * @param {String} text The title text to display
29006      * @return {Roo.BasicDialog} this
29007      */
29008     setTitle : function(text){
29009         this.header.update(text);
29010         return this;
29011     },
29012
29013     // private
29014     closeClick : function(){
29015         this.hide();
29016     },
29017
29018     // private
29019     collapseClick : function(){
29020         this[this.collapsed ? "expand" : "collapse"]();
29021     },
29022
29023     /**
29024      * Collapses the dialog to its minimized state (only the title bar is visible).
29025      * Equivalent to the user clicking the collapse dialog button.
29026      */
29027     collapse : function(){
29028         if(!this.collapsed){
29029             this.collapsed = true;
29030             this.el.addClass("x-dlg-collapsed");
29031             this.restoreHeight = this.el.getHeight();
29032             this.resizeTo(this.el.getWidth(), this.header.getHeight());
29033         }
29034     },
29035
29036     /**
29037      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
29038      * clicking the expand dialog button.
29039      */
29040     expand : function(){
29041         if(this.collapsed){
29042             this.collapsed = false;
29043             this.el.removeClass("x-dlg-collapsed");
29044             this.resizeTo(this.el.getWidth(), this.restoreHeight);
29045         }
29046     },
29047
29048     /**
29049      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
29050      * @return {Roo.TabPanel} The tabs component
29051      */
29052     initTabs : function(){
29053         var tabs = this.getTabs();
29054         while(tabs.getTab(0)){
29055             tabs.removeTab(0);
29056         }
29057         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
29058             var dom = el.dom;
29059             tabs.addTab(Roo.id(dom), dom.title);
29060             dom.title = "";
29061         });
29062         tabs.activate(0);
29063         return tabs;
29064     },
29065
29066     // private
29067     beforeResize : function(){
29068         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
29069     },
29070
29071     // private
29072     onResize : function(){
29073         this.refreshSize();
29074         this.syncBodyHeight();
29075         this.adjustAssets();
29076         this.focus();
29077         this.fireEvent("resize", this, this.size.width, this.size.height);
29078     },
29079
29080     // private
29081     onKeyDown : function(e){
29082         if(this.isVisible()){
29083             this.fireEvent("keydown", this, e);
29084         }
29085     },
29086
29087     /**
29088      * Resizes the dialog.
29089      * @param {Number} width
29090      * @param {Number} height
29091      * @return {Roo.BasicDialog} this
29092      */
29093     resizeTo : function(width, height){
29094         this.el.setSize(width, height);
29095         this.size = {width: width, height: height};
29096         this.syncBodyHeight();
29097         if(this.fixedcenter){
29098             this.center();
29099         }
29100         if(this.isVisible()){
29101             this.constrainXY();
29102             this.adjustAssets();
29103         }
29104         this.fireEvent("resize", this, width, height);
29105         return this;
29106     },
29107
29108
29109     /**
29110      * Resizes the dialog to fit the specified content size.
29111      * @param {Number} width
29112      * @param {Number} height
29113      * @return {Roo.BasicDialog} this
29114      */
29115     setContentSize : function(w, h){
29116         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
29117         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
29118         //if(!this.el.isBorderBox()){
29119             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
29120             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
29121         //}
29122         if(this.tabs){
29123             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
29124             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
29125         }
29126         this.resizeTo(w, h);
29127         return this;
29128     },
29129
29130     /**
29131      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
29132      * executed in response to a particular key being pressed while the dialog is active.
29133      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
29134      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
29135      * @param {Function} fn The function to call
29136      * @param {Object} scope (optional) The scope of the function
29137      * @return {Roo.BasicDialog} this
29138      */
29139     addKeyListener : function(key, fn, scope){
29140         var keyCode, shift, ctrl, alt;
29141         if(typeof key == "object" && !(key instanceof Array)){
29142             keyCode = key["key"];
29143             shift = key["shift"];
29144             ctrl = key["ctrl"];
29145             alt = key["alt"];
29146         }else{
29147             keyCode = key;
29148         }
29149         var handler = function(dlg, e){
29150             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
29151                 var k = e.getKey();
29152                 if(keyCode instanceof Array){
29153                     for(var i = 0, len = keyCode.length; i < len; i++){
29154                         if(keyCode[i] == k){
29155                           fn.call(scope || window, dlg, k, e);
29156                           return;
29157                         }
29158                     }
29159                 }else{
29160                     if(k == keyCode){
29161                         fn.call(scope || window, dlg, k, e);
29162                     }
29163                 }
29164             }
29165         };
29166         this.on("keydown", handler);
29167         return this;
29168     },
29169
29170     /**
29171      * Returns the TabPanel component (creates it if it doesn't exist).
29172      * Note: If you wish to simply check for the existence of tabs without creating them,
29173      * check for a null 'tabs' property.
29174      * @return {Roo.TabPanel} The tabs component
29175      */
29176     getTabs : function(){
29177         if(!this.tabs){
29178             this.el.addClass("x-dlg-auto-tabs");
29179             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
29180             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
29181         }
29182         return this.tabs;
29183     },
29184
29185     /**
29186      * Adds a button to the footer section of the dialog.
29187      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
29188      * object or a valid Roo.DomHelper element config
29189      * @param {Function} handler The function called when the button is clicked
29190      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
29191      * @return {Roo.Button} The new button
29192      */
29193     addButton : function(config, handler, scope){
29194         var dh = Roo.DomHelper;
29195         if(!this.footer){
29196             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
29197         }
29198         if(!this.btnContainer){
29199             var tb = this.footer.createChild({
29200
29201                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
29202                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
29203             }, null, true);
29204             this.btnContainer = tb.firstChild.firstChild.firstChild;
29205         }
29206         var bconfig = {
29207             handler: handler,
29208             scope: scope,
29209             minWidth: this.minButtonWidth,
29210             hideParent:true
29211         };
29212         if(typeof config == "string"){
29213             bconfig.text = config;
29214         }else{
29215             if(config.tag){
29216                 bconfig.dhconfig = config;
29217             }else{
29218                 Roo.apply(bconfig, config);
29219             }
29220         }
29221         var fc = false;
29222         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
29223             bconfig.position = Math.max(0, bconfig.position);
29224             fc = this.btnContainer.childNodes[bconfig.position];
29225         }
29226          
29227         var btn = new Roo.Button(
29228             fc ? 
29229                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29230                 : this.btnContainer.appendChild(document.createElement("td")),
29231             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29232             bconfig
29233         );
29234         this.syncBodyHeight();
29235         if(!this.buttons){
29236             /**
29237              * Array of all the buttons that have been added to this dialog via addButton
29238              * @type Array
29239              */
29240             this.buttons = [];
29241         }
29242         this.buttons.push(btn);
29243         return btn;
29244     },
29245
29246     /**
29247      * Sets the default button to be focused when the dialog is displayed.
29248      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29249      * @return {Roo.BasicDialog} this
29250      */
29251     setDefaultButton : function(btn){
29252         this.defaultButton = btn;
29253         return this;
29254     },
29255
29256     // private
29257     getHeaderFooterHeight : function(safe){
29258         var height = 0;
29259         if(this.header){
29260            height += this.header.getHeight();
29261         }
29262         if(this.footer){
29263            var fm = this.footer.getMargins();
29264             height += (this.footer.getHeight()+fm.top+fm.bottom);
29265         }
29266         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29267         height += this.centerBg.getPadding("tb");
29268         return height;
29269     },
29270
29271     // private
29272     syncBodyHeight : function(){
29273         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29274         var height = this.size.height - this.getHeaderFooterHeight(false);
29275         bd.setHeight(height-bd.getMargins("tb"));
29276         var hh = this.header.getHeight();
29277         var h = this.size.height-hh;
29278         cb.setHeight(h);
29279         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29280         bw.setHeight(h-cb.getPadding("tb"));
29281         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29282         bd.setWidth(bw.getWidth(true));
29283         if(this.tabs){
29284             this.tabs.syncHeight();
29285             if(Roo.isIE){
29286                 this.tabs.el.repaint();
29287             }
29288         }
29289     },
29290
29291     /**
29292      * Restores the previous state of the dialog if Roo.state is configured.
29293      * @return {Roo.BasicDialog} this
29294      */
29295     restoreState : function(){
29296         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29297         if(box && box.width){
29298             this.xy = [box.x, box.y];
29299             this.resizeTo(box.width, box.height);
29300         }
29301         return this;
29302     },
29303
29304     // private
29305     beforeShow : function(){
29306         this.expand();
29307         if(this.fixedcenter){
29308             this.xy = this.el.getCenterXY(true);
29309         }
29310         if(this.modal){
29311             Roo.get(document.body).addClass("x-body-masked");
29312             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29313             this.mask.show();
29314         }
29315         this.constrainXY();
29316     },
29317
29318     // private
29319     animShow : function(){
29320         var b = Roo.get(this.animateTarget).getBox();
29321         this.proxy.setSize(b.width, b.height);
29322         this.proxy.setLocation(b.x, b.y);
29323         this.proxy.show();
29324         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29325                     true, .35, this.showEl.createDelegate(this));
29326     },
29327
29328     /**
29329      * Shows the dialog.
29330      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29331      * @return {Roo.BasicDialog} this
29332      */
29333     show : function(animateTarget){
29334         if (this.fireEvent("beforeshow", this) === false){
29335             return;
29336         }
29337         if(this.syncHeightBeforeShow){
29338             this.syncBodyHeight();
29339         }else if(this.firstShow){
29340             this.firstShow = false;
29341             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29342         }
29343         this.animateTarget = animateTarget || this.animateTarget;
29344         if(!this.el.isVisible()){
29345             this.beforeShow();
29346             if(this.animateTarget && Roo.get(this.animateTarget)){
29347                 this.animShow();
29348             }else{
29349                 this.showEl();
29350             }
29351         }
29352         return this;
29353     },
29354
29355     // private
29356     showEl : function(){
29357         this.proxy.hide();
29358         this.el.setXY(this.xy);
29359         this.el.show();
29360         this.adjustAssets(true);
29361         this.toFront();
29362         this.focus();
29363         // IE peekaboo bug - fix found by Dave Fenwick
29364         if(Roo.isIE){
29365             this.el.repaint();
29366         }
29367         this.fireEvent("show", this);
29368     },
29369
29370     /**
29371      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29372      * dialog itself will receive focus.
29373      */
29374     focus : function(){
29375         if(this.defaultButton){
29376             this.defaultButton.focus();
29377         }else{
29378             this.focusEl.focus();
29379         }
29380     },
29381
29382     // private
29383     constrainXY : function(){
29384         if(this.constraintoviewport !== false){
29385             if(!this.viewSize){
29386                 if(this.container){
29387                     var s = this.container.getSize();
29388                     this.viewSize = [s.width, s.height];
29389                 }else{
29390                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29391                 }
29392             }
29393             var s = Roo.get(this.container||document).getScroll();
29394
29395             var x = this.xy[0], y = this.xy[1];
29396             var w = this.size.width, h = this.size.height;
29397             var vw = this.viewSize[0], vh = this.viewSize[1];
29398             // only move it if it needs it
29399             var moved = false;
29400             // first validate right/bottom
29401             if(x + w > vw+s.left){
29402                 x = vw - w;
29403                 moved = true;
29404             }
29405             if(y + h > vh+s.top){
29406                 y = vh - h;
29407                 moved = true;
29408             }
29409             // then make sure top/left isn't negative
29410             if(x < s.left){
29411                 x = s.left;
29412                 moved = true;
29413             }
29414             if(y < s.top){
29415                 y = s.top;
29416                 moved = true;
29417             }
29418             if(moved){
29419                 // cache xy
29420                 this.xy = [x, y];
29421                 if(this.isVisible()){
29422                     this.el.setLocation(x, y);
29423                     this.adjustAssets();
29424                 }
29425             }
29426         }
29427     },
29428
29429     // private
29430     onDrag : function(){
29431         if(!this.proxyDrag){
29432             this.xy = this.el.getXY();
29433             this.adjustAssets();
29434         }
29435     },
29436
29437     // private
29438     adjustAssets : function(doShow){
29439         var x = this.xy[0], y = this.xy[1];
29440         var w = this.size.width, h = this.size.height;
29441         if(doShow === true){
29442             if(this.shadow){
29443                 this.shadow.show(this.el);
29444             }
29445             if(this.shim){
29446                 this.shim.show();
29447             }
29448         }
29449         if(this.shadow && this.shadow.isVisible()){
29450             this.shadow.show(this.el);
29451         }
29452         if(this.shim && this.shim.isVisible()){
29453             this.shim.setBounds(x, y, w, h);
29454         }
29455     },
29456
29457     // private
29458     adjustViewport : function(w, h){
29459         if(!w || !h){
29460             w = Roo.lib.Dom.getViewWidth();
29461             h = Roo.lib.Dom.getViewHeight();
29462         }
29463         // cache the size
29464         this.viewSize = [w, h];
29465         if(this.modal && this.mask.isVisible()){
29466             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29467             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29468         }
29469         if(this.isVisible()){
29470             this.constrainXY();
29471         }
29472     },
29473
29474     /**
29475      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29476      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29477      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29478      */
29479     destroy : function(removeEl){
29480         if(this.isVisible()){
29481             this.animateTarget = null;
29482             this.hide();
29483         }
29484         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29485         if(this.tabs){
29486             this.tabs.destroy(removeEl);
29487         }
29488         Roo.destroy(
29489              this.shim,
29490              this.proxy,
29491              this.resizer,
29492              this.close,
29493              this.mask
29494         );
29495         if(this.dd){
29496             this.dd.unreg();
29497         }
29498         if(this.buttons){
29499            for(var i = 0, len = this.buttons.length; i < len; i++){
29500                this.buttons[i].destroy();
29501            }
29502         }
29503         this.el.removeAllListeners();
29504         if(removeEl === true){
29505             this.el.update("");
29506             this.el.remove();
29507         }
29508         Roo.DialogManager.unregister(this);
29509     },
29510
29511     // private
29512     startMove : function(){
29513         if(this.proxyDrag){
29514             this.proxy.show();
29515         }
29516         if(this.constraintoviewport !== false){
29517             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29518         }
29519     },
29520
29521     // private
29522     endMove : function(){
29523         if(!this.proxyDrag){
29524             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29525         }else{
29526             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29527             this.proxy.hide();
29528         }
29529         this.refreshSize();
29530         this.adjustAssets();
29531         this.focus();
29532         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29533     },
29534
29535     /**
29536      * Brings this dialog to the front of any other visible dialogs
29537      * @return {Roo.BasicDialog} this
29538      */
29539     toFront : function(){
29540         Roo.DialogManager.bringToFront(this);
29541         return this;
29542     },
29543
29544     /**
29545      * Sends this dialog to the back (under) of any other visible dialogs
29546      * @return {Roo.BasicDialog} this
29547      */
29548     toBack : function(){
29549         Roo.DialogManager.sendToBack(this);
29550         return this;
29551     },
29552
29553     /**
29554      * Centers this dialog in the viewport
29555      * @return {Roo.BasicDialog} this
29556      */
29557     center : function(){
29558         var xy = this.el.getCenterXY(true);
29559         this.moveTo(xy[0], xy[1]);
29560         return this;
29561     },
29562
29563     /**
29564      * Moves the dialog's top-left corner to the specified point
29565      * @param {Number} x
29566      * @param {Number} y
29567      * @return {Roo.BasicDialog} this
29568      */
29569     moveTo : function(x, y){
29570         this.xy = [x,y];
29571         if(this.isVisible()){
29572             this.el.setXY(this.xy);
29573             this.adjustAssets();
29574         }
29575         return this;
29576     },
29577
29578     /**
29579      * Aligns the dialog to the specified element
29580      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29581      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29582      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29583      * @return {Roo.BasicDialog} this
29584      */
29585     alignTo : function(element, position, offsets){
29586         this.xy = this.el.getAlignToXY(element, position, offsets);
29587         if(this.isVisible()){
29588             this.el.setXY(this.xy);
29589             this.adjustAssets();
29590         }
29591         return this;
29592     },
29593
29594     /**
29595      * Anchors an element to another element and realigns it when the window is resized.
29596      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29597      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29598      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29599      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29600      * is a number, it is used as the buffer delay (defaults to 50ms).
29601      * @return {Roo.BasicDialog} this
29602      */
29603     anchorTo : function(el, alignment, offsets, monitorScroll){
29604         var action = function(){
29605             this.alignTo(el, alignment, offsets);
29606         };
29607         Roo.EventManager.onWindowResize(action, this);
29608         var tm = typeof monitorScroll;
29609         if(tm != 'undefined'){
29610             Roo.EventManager.on(window, 'scroll', action, this,
29611                 {buffer: tm == 'number' ? monitorScroll : 50});
29612         }
29613         action.call(this);
29614         return this;
29615     },
29616
29617     /**
29618      * Returns true if the dialog is visible
29619      * @return {Boolean}
29620      */
29621     isVisible : function(){
29622         return this.el.isVisible();
29623     },
29624
29625     // private
29626     animHide : function(callback){
29627         var b = Roo.get(this.animateTarget).getBox();
29628         this.proxy.show();
29629         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29630         this.el.hide();
29631         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29632                     this.hideEl.createDelegate(this, [callback]));
29633     },
29634
29635     /**
29636      * Hides the dialog.
29637      * @param {Function} callback (optional) Function to call when the dialog is hidden
29638      * @return {Roo.BasicDialog} this
29639      */
29640     hide : function(callback){
29641         if (this.fireEvent("beforehide", this) === false){
29642             return;
29643         }
29644         if(this.shadow){
29645             this.shadow.hide();
29646         }
29647         if(this.shim) {
29648           this.shim.hide();
29649         }
29650         // sometimes animateTarget seems to get set.. causing problems...
29651         // this just double checks..
29652         if(this.animateTarget && Roo.get(this.animateTarget)) {
29653            this.animHide(callback);
29654         }else{
29655             this.el.hide();
29656             this.hideEl(callback);
29657         }
29658         return this;
29659     },
29660
29661     // private
29662     hideEl : function(callback){
29663         this.proxy.hide();
29664         if(this.modal){
29665             this.mask.hide();
29666             Roo.get(document.body).removeClass("x-body-masked");
29667         }
29668         this.fireEvent("hide", this);
29669         if(typeof callback == "function"){
29670             callback();
29671         }
29672     },
29673
29674     // private
29675     hideAction : function(){
29676         this.setLeft("-10000px");
29677         this.setTop("-10000px");
29678         this.setStyle("visibility", "hidden");
29679     },
29680
29681     // private
29682     refreshSize : function(){
29683         this.size = this.el.getSize();
29684         this.xy = this.el.getXY();
29685         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29686     },
29687
29688     // private
29689     // z-index is managed by the DialogManager and may be overwritten at any time
29690     setZIndex : function(index){
29691         if(this.modal){
29692             this.mask.setStyle("z-index", index);
29693         }
29694         if(this.shim){
29695             this.shim.setStyle("z-index", ++index);
29696         }
29697         if(this.shadow){
29698             this.shadow.setZIndex(++index);
29699         }
29700         this.el.setStyle("z-index", ++index);
29701         if(this.proxy){
29702             this.proxy.setStyle("z-index", ++index);
29703         }
29704         if(this.resizer){
29705             this.resizer.proxy.setStyle("z-index", ++index);
29706         }
29707
29708         this.lastZIndex = index;
29709     },
29710
29711     /**
29712      * Returns the element for this dialog
29713      * @return {Roo.Element} The underlying dialog Element
29714      */
29715     getEl : function(){
29716         return this.el;
29717     }
29718 });
29719
29720 /**
29721  * @class Roo.DialogManager
29722  * Provides global access to BasicDialogs that have been created and
29723  * support for z-indexing (layering) multiple open dialogs.
29724  */
29725 Roo.DialogManager = function(){
29726     var list = {};
29727     var accessList = [];
29728     var front = null;
29729
29730     // private
29731     var sortDialogs = function(d1, d2){
29732         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29733     };
29734
29735     // private
29736     var orderDialogs = function(){
29737         accessList.sort(sortDialogs);
29738         var seed = Roo.DialogManager.zseed;
29739         for(var i = 0, len = accessList.length; i < len; i++){
29740             var dlg = accessList[i];
29741             if(dlg){
29742                 dlg.setZIndex(seed + (i*10));
29743             }
29744         }
29745     };
29746
29747     return {
29748         /**
29749          * The starting z-index for BasicDialogs (defaults to 9000)
29750          * @type Number The z-index value
29751          */
29752         zseed : 9000,
29753
29754         // private
29755         register : function(dlg){
29756             list[dlg.id] = dlg;
29757             accessList.push(dlg);
29758         },
29759
29760         // private
29761         unregister : function(dlg){
29762             delete list[dlg.id];
29763             var i=0;
29764             var len=0;
29765             if(!accessList.indexOf){
29766                 for(  i = 0, len = accessList.length; i < len; i++){
29767                     if(accessList[i] == dlg){
29768                         accessList.splice(i, 1);
29769                         return;
29770                     }
29771                 }
29772             }else{
29773                  i = accessList.indexOf(dlg);
29774                 if(i != -1){
29775                     accessList.splice(i, 1);
29776                 }
29777             }
29778         },
29779
29780         /**
29781          * Gets a registered dialog by id
29782          * @param {String/Object} id The id of the dialog or a dialog
29783          * @return {Roo.BasicDialog} this
29784          */
29785         get : function(id){
29786             return typeof id == "object" ? id : list[id];
29787         },
29788
29789         /**
29790          * Brings the specified dialog to the front
29791          * @param {String/Object} dlg The id of the dialog or a dialog
29792          * @return {Roo.BasicDialog} this
29793          */
29794         bringToFront : function(dlg){
29795             dlg = this.get(dlg);
29796             if(dlg != front){
29797                 front = dlg;
29798                 dlg._lastAccess = new Date().getTime();
29799                 orderDialogs();
29800             }
29801             return dlg;
29802         },
29803
29804         /**
29805          * Sends the specified dialog to the back
29806          * @param {String/Object} dlg The id of the dialog or a dialog
29807          * @return {Roo.BasicDialog} this
29808          */
29809         sendToBack : function(dlg){
29810             dlg = this.get(dlg);
29811             dlg._lastAccess = -(new Date().getTime());
29812             orderDialogs();
29813             return dlg;
29814         },
29815
29816         /**
29817          * Hides all dialogs
29818          */
29819         hideAll : function(){
29820             for(var id in list){
29821                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29822                     list[id].hide();
29823                 }
29824             }
29825         }
29826     };
29827 }();
29828
29829 /**
29830  * @class Roo.LayoutDialog
29831  * @extends Roo.BasicDialog
29832  * Dialog which provides adjustments for working with a layout in a Dialog.
29833  * Add your necessary layout config options to the dialog's config.<br>
29834  * Example usage (including a nested layout):
29835  * <pre><code>
29836 if(!dialog){
29837     dialog = new Roo.LayoutDialog("download-dlg", {
29838         modal: true,
29839         width:600,
29840         height:450,
29841         shadow:true,
29842         minWidth:500,
29843         minHeight:350,
29844         autoTabs:true,
29845         proxyDrag:true,
29846         // layout config merges with the dialog config
29847         center:{
29848             tabPosition: "top",
29849             alwaysShowTabs: true
29850         }
29851     });
29852     dialog.addKeyListener(27, dialog.hide, dialog);
29853     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29854     dialog.addButton("Build It!", this.getDownload, this);
29855
29856     // we can even add nested layouts
29857     var innerLayout = new Roo.BorderLayout("dl-inner", {
29858         east: {
29859             initialSize: 200,
29860             autoScroll:true,
29861             split:true
29862         },
29863         center: {
29864             autoScroll:true
29865         }
29866     });
29867     innerLayout.beginUpdate();
29868     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29869     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29870     innerLayout.endUpdate(true);
29871
29872     var layout = dialog.getLayout();
29873     layout.beginUpdate();
29874     layout.add("center", new Roo.ContentPanel("standard-panel",
29875                         {title: "Download the Source", fitToFrame:true}));
29876     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29877                {title: "Build your own roo.js"}));
29878     layout.getRegion("center").showPanel(sp);
29879     layout.endUpdate();
29880 }
29881 </code></pre>
29882     * @constructor
29883     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29884     * @param {Object} config configuration options
29885   */
29886 Roo.LayoutDialog = function(el, cfg){
29887     
29888     var config=  cfg;
29889     if (typeof(cfg) == 'undefined') {
29890         config = Roo.apply({}, el);
29891         // not sure why we use documentElement here.. - it should always be body.
29892         // IE7 borks horribly if we use documentElement.
29893         // webkit also does not like documentElement - it creates a body element...
29894         el = Roo.get( document.body || document.documentElement ).createChild();
29895         //config.autoCreate = true;
29896     }
29897     
29898     
29899     config.autoTabs = false;
29900     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29901     this.body.setStyle({overflow:"hidden", position:"relative"});
29902     this.layout = new Roo.BorderLayout(this.body.dom, config);
29903     this.layout.monitorWindowResize = false;
29904     this.el.addClass("x-dlg-auto-layout");
29905     // fix case when center region overwrites center function
29906     this.center = Roo.BasicDialog.prototype.center;
29907     this.on("show", this.layout.layout, this.layout, true);
29908     if (config.items) {
29909         var xitems = config.items;
29910         delete config.items;
29911         Roo.each(xitems, this.addxtype, this);
29912     }
29913     
29914     
29915 };
29916 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29917     /**
29918      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29919      * @deprecated
29920      */
29921     endUpdate : function(){
29922         this.layout.endUpdate();
29923     },
29924
29925     /**
29926      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29927      *  @deprecated
29928      */
29929     beginUpdate : function(){
29930         this.layout.beginUpdate();
29931     },
29932
29933     /**
29934      * Get the BorderLayout for this dialog
29935      * @return {Roo.BorderLayout}
29936      */
29937     getLayout : function(){
29938         return this.layout;
29939     },
29940
29941     showEl : function(){
29942         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29943         if(Roo.isIE7){
29944             this.layout.layout();
29945         }
29946     },
29947
29948     // private
29949     // Use the syncHeightBeforeShow config option to control this automatically
29950     syncBodyHeight : function(){
29951         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29952         if(this.layout){this.layout.layout();}
29953     },
29954     
29955       /**
29956      * Add an xtype element (actually adds to the layout.)
29957      * @return {Object} xdata xtype object data.
29958      */
29959     
29960     addxtype : function(c) {
29961         return this.layout.addxtype(c);
29962     }
29963 });/*
29964  * Based on:
29965  * Ext JS Library 1.1.1
29966  * Copyright(c) 2006-2007, Ext JS, LLC.
29967  *
29968  * Originally Released Under LGPL - original licence link has changed is not relivant.
29969  *
29970  * Fork - LGPL
29971  * <script type="text/javascript">
29972  */
29973  
29974 /**
29975  * @class Roo.MessageBox
29976  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29977  * Example usage:
29978  *<pre><code>
29979 // Basic alert:
29980 Roo.Msg.alert('Status', 'Changes saved successfully.');
29981
29982 // Prompt for user data:
29983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29984     if (btn == 'ok'){
29985         // process text value...
29986     }
29987 });
29988
29989 // Show a dialog using config options:
29990 Roo.Msg.show({
29991    title:'Save Changes?',
29992    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29993    buttons: Roo.Msg.YESNOCANCEL,
29994    fn: processResult,
29995    animEl: 'elId'
29996 });
29997 </code></pre>
29998  * @singleton
29999  */
30000 Roo.MessageBox = function(){
30001     var dlg, opt, mask, waitTimer;
30002     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
30003     var buttons, activeTextEl, bwidth;
30004
30005     // private
30006     var handleButton = function(button){
30007         dlg.hide();
30008         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
30009     };
30010
30011     // private
30012     var handleHide = function(){
30013         if(opt && opt.cls){
30014             dlg.el.removeClass(opt.cls);
30015         }
30016         if(waitTimer){
30017             Roo.TaskMgr.stop(waitTimer);
30018             waitTimer = null;
30019         }
30020     };
30021
30022     // private
30023     var updateButtons = function(b){
30024         var width = 0;
30025         if(!b){
30026             buttons["ok"].hide();
30027             buttons["cancel"].hide();
30028             buttons["yes"].hide();
30029             buttons["no"].hide();
30030             dlg.footer.dom.style.display = 'none';
30031             return width;
30032         }
30033         dlg.footer.dom.style.display = '';
30034         for(var k in buttons){
30035             if(typeof buttons[k] != "function"){
30036                 if(b[k]){
30037                     buttons[k].show();
30038                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
30039                     width += buttons[k].el.getWidth()+15;
30040                 }else{
30041                     buttons[k].hide();
30042                 }
30043             }
30044         }
30045         return width;
30046     };
30047
30048     // private
30049     var handleEsc = function(d, k, e){
30050         if(opt && opt.closable !== false){
30051             dlg.hide();
30052         }
30053         if(e){
30054             e.stopEvent();
30055         }
30056     };
30057
30058     return {
30059         /**
30060          * Returns a reference to the underlying {@link Roo.BasicDialog} element
30061          * @return {Roo.BasicDialog} The BasicDialog element
30062          */
30063         getDialog : function(){
30064            if(!dlg){
30065                 dlg = new Roo.BasicDialog("x-msg-box", {
30066                     autoCreate : true,
30067                     shadow: true,
30068                     draggable: true,
30069                     resizable:false,
30070                     constraintoviewport:false,
30071                     fixedcenter:true,
30072                     collapsible : false,
30073                     shim:true,
30074                     modal: true,
30075                     width:400, height:100,
30076                     buttonAlign:"center",
30077                     closeClick : function(){
30078                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
30079                             handleButton("no");
30080                         }else{
30081                             handleButton("cancel");
30082                         }
30083                     }
30084                 });
30085                 dlg.on("hide", handleHide);
30086                 mask = dlg.mask;
30087                 dlg.addKeyListener(27, handleEsc);
30088                 buttons = {};
30089                 var bt = this.buttonText;
30090                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
30091                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
30092                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
30093                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
30094                 bodyEl = dlg.body.createChild({
30095
30096                     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>'
30097                 });
30098                 msgEl = bodyEl.dom.firstChild;
30099                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
30100                 textboxEl.enableDisplayMode();
30101                 textboxEl.addKeyListener([10,13], function(){
30102                     if(dlg.isVisible() && opt && opt.buttons){
30103                         if(opt.buttons.ok){
30104                             handleButton("ok");
30105                         }else if(opt.buttons.yes){
30106                             handleButton("yes");
30107                         }
30108                     }
30109                 });
30110                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
30111                 textareaEl.enableDisplayMode();
30112                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
30113                 progressEl.enableDisplayMode();
30114                 var pf = progressEl.dom.firstChild;
30115                 if (pf) {
30116                     pp = Roo.get(pf.firstChild);
30117                     pp.setHeight(pf.offsetHeight);
30118                 }
30119                 
30120             }
30121             return dlg;
30122         },
30123
30124         /**
30125          * Updates the message box body text
30126          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
30127          * the XHTML-compliant non-breaking space character '&amp;#160;')
30128          * @return {Roo.MessageBox} This message box
30129          */
30130         updateText : function(text){
30131             if(!dlg.isVisible() && !opt.width){
30132                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
30133             }
30134             msgEl.innerHTML = text || '&#160;';
30135             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
30136                         Math.max(opt.minWidth || this.minWidth, bwidth));
30137             if(opt.prompt){
30138                 activeTextEl.setWidth(w);
30139             }
30140             if(dlg.isVisible()){
30141                 dlg.fixedcenter = false;
30142             }
30143             dlg.setContentSize(w, bodyEl.getHeight());
30144             if(dlg.isVisible()){
30145                 dlg.fixedcenter = true;
30146             }
30147             return this;
30148         },
30149
30150         /**
30151          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
30152          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
30153          * @param {Number} value Any number between 0 and 1 (e.g., .5)
30154          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
30155          * @return {Roo.MessageBox} This message box
30156          */
30157         updateProgress : function(value, text){
30158             if(text){
30159                 this.updateText(text);
30160             }
30161             if (pp) { // weird bug on my firefox - for some reason this is not defined
30162                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
30163             }
30164             return this;
30165         },        
30166
30167         /**
30168          * Returns true if the message box is currently displayed
30169          * @return {Boolean} True if the message box is visible, else false
30170          */
30171         isVisible : function(){
30172             return dlg && dlg.isVisible();  
30173         },
30174
30175         /**
30176          * Hides the message box if it is displayed
30177          */
30178         hide : function(){
30179             if(this.isVisible()){
30180                 dlg.hide();
30181             }  
30182         },
30183
30184         /**
30185          * Displays a new message box, or reinitializes an existing message box, based on the config options
30186          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
30187          * The following config object properties are supported:
30188          * <pre>
30189 Property    Type             Description
30190 ----------  ---------------  ------------------------------------------------------------------------------------
30191 animEl            String/Element   An id or Element from which the message box should animate as it opens and
30192                                    closes (defaults to undefined)
30193 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
30194                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
30195 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
30196                                    progress and wait dialogs will ignore this property and always hide the
30197                                    close button as they can only be closed programmatically.
30198 cls               String           A custom CSS class to apply to the message box element
30199 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
30200                                    displayed (defaults to 75)
30201 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
30202                                    function will be btn (the name of the button that was clicked, if applicable,
30203                                    e.g. "ok"), and text (the value of the active text field, if applicable).
30204                                    Progress and wait dialogs will ignore this option since they do not respond to
30205                                    user actions and can only be closed programmatically, so any required function
30206                                    should be called by the same code after it closes the dialog.
30207 icon              String           A CSS class that provides a background image to be used as an icon for
30208                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
30209 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
30210 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
30211 modal             Boolean          False to allow user interaction with the page while the message box is
30212                                    displayed (defaults to true)
30213 msg               String           A string that will replace the existing message box body text (defaults
30214                                    to the XHTML-compliant non-breaking space character '&#160;')
30215 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
30216 progress          Boolean          True to display a progress bar (defaults to false)
30217 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
30218 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
30219 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
30220 title             String           The title text
30221 value             String           The string value to set into the active textbox element if displayed
30222 wait              Boolean          True to display a progress bar (defaults to false)
30223 width             Number           The width of the dialog in pixels
30224 </pre>
30225          *
30226          * Example usage:
30227          * <pre><code>
30228 Roo.Msg.show({
30229    title: 'Address',
30230    msg: 'Please enter your address:',
30231    width: 300,
30232    buttons: Roo.MessageBox.OKCANCEL,
30233    multiline: true,
30234    fn: saveAddress,
30235    animEl: 'addAddressBtn'
30236 });
30237 </code></pre>
30238          * @param {Object} config Configuration options
30239          * @return {Roo.MessageBox} This message box
30240          */
30241         show : function(options)
30242         {
30243             
30244             // this causes nightmares if you show one dialog after another
30245             // especially on callbacks..
30246              
30247             if(this.isVisible()){
30248                 
30249                 this.hide();
30250                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML )
30251                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
30252                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
30253                 
30254             }
30255             var d = this.getDialog();
30256             opt = options;
30257             d.setTitle(opt.title || "&#160;");
30258             d.close.setDisplayed(opt.closable !== false);
30259             activeTextEl = textboxEl;
30260             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30261             if(opt.prompt){
30262                 if(opt.multiline){
30263                     textboxEl.hide();
30264                     textareaEl.show();
30265                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30266                         opt.multiline : this.defaultTextHeight);
30267                     activeTextEl = textareaEl;
30268                 }else{
30269                     textboxEl.show();
30270                     textareaEl.hide();
30271                 }
30272             }else{
30273                 textboxEl.hide();
30274                 textareaEl.hide();
30275             }
30276             progressEl.setDisplayed(opt.progress === true);
30277             this.updateProgress(0);
30278             activeTextEl.dom.value = opt.value || "";
30279             if(opt.prompt){
30280                 dlg.setDefaultButton(activeTextEl);
30281             }else{
30282                 var bs = opt.buttons;
30283                 var db = null;
30284                 if(bs && bs.ok){
30285                     db = buttons["ok"];
30286                 }else if(bs && bs.yes){
30287                     db = buttons["yes"];
30288                 }
30289                 dlg.setDefaultButton(db);
30290             }
30291             bwidth = updateButtons(opt.buttons);
30292             this.updateText(opt.msg);
30293             if(opt.cls){
30294                 d.el.addClass(opt.cls);
30295             }
30296             d.proxyDrag = opt.proxyDrag === true;
30297             d.modal = opt.modal !== false;
30298             d.mask = opt.modal !== false ? mask : false;
30299             if(!d.isVisible()){
30300                 // force it to the end of the z-index stack so it gets a cursor in FF
30301                 document.body.appendChild(dlg.el.dom);
30302                 d.animateTarget = null;
30303                 d.show(options.animEl);
30304             }
30305             return this;
30306         },
30307
30308         /**
30309          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30310          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30311          * and closing the message box when the process is complete.
30312          * @param {String} title The title bar text
30313          * @param {String} msg The message box body text
30314          * @return {Roo.MessageBox} This message box
30315          */
30316         progress : function(title, msg){
30317             this.show({
30318                 title : title,
30319                 msg : msg,
30320                 buttons: false,
30321                 progress:true,
30322                 closable:false,
30323                 minWidth: this.minProgressWidth,
30324                 modal : true
30325             });
30326             return this;
30327         },
30328
30329         /**
30330          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30331          * If a callback function is passed it will be called after the user clicks the button, and the
30332          * id of the button that was clicked will be passed as the only parameter to the callback
30333          * (could also be the top-right close button).
30334          * @param {String} title The title bar text
30335          * @param {String} msg The message box body text
30336          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30337          * @param {Object} scope (optional) The scope of the callback function
30338          * @return {Roo.MessageBox} This message box
30339          */
30340         alert : function(title, msg, fn, scope){
30341             this.show({
30342                 title : title,
30343                 msg : msg,
30344                 buttons: this.OK,
30345                 fn: fn,
30346                 scope : scope,
30347                 modal : true
30348             });
30349             return this;
30350         },
30351
30352         /**
30353          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30354          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30355          * You are responsible for closing the message box when the process is complete.
30356          * @param {String} msg The message box body text
30357          * @param {String} title (optional) The title bar text
30358          * @return {Roo.MessageBox} This message box
30359          */
30360         wait : function(msg, title){
30361             this.show({
30362                 title : title,
30363                 msg : msg,
30364                 buttons: false,
30365                 closable:false,
30366                 progress:true,
30367                 modal:true,
30368                 width:300,
30369                 wait:true
30370             });
30371             waitTimer = Roo.TaskMgr.start({
30372                 run: function(i){
30373                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30374                 },
30375                 interval: 1000
30376             });
30377             return this;
30378         },
30379
30380         /**
30381          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30382          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30383          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30384          * @param {String} title The title bar text
30385          * @param {String} msg The message box body text
30386          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30387          * @param {Object} scope (optional) The scope of the callback function
30388          * @return {Roo.MessageBox} This message box
30389          */
30390         confirm : function(title, msg, fn, scope){
30391             this.show({
30392                 title : title,
30393                 msg : msg,
30394                 buttons: this.YESNO,
30395                 fn: fn,
30396                 scope : scope,
30397                 modal : true
30398             });
30399             return this;
30400         },
30401
30402         /**
30403          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30404          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30405          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30406          * (could also be the top-right close button) and the text that was entered will be passed as the two
30407          * parameters to the callback.
30408          * @param {String} title The title bar text
30409          * @param {String} msg The message box body text
30410          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30411          * @param {Object} scope (optional) The scope of the callback function
30412          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30413          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30414          * @return {Roo.MessageBox} This message box
30415          */
30416         prompt : function(title, msg, fn, scope, multiline){
30417             this.show({
30418                 title : title,
30419                 msg : msg,
30420                 buttons: this.OKCANCEL,
30421                 fn: fn,
30422                 minWidth:250,
30423                 scope : scope,
30424                 prompt:true,
30425                 multiline: multiline,
30426                 modal : true
30427             });
30428             return this;
30429         },
30430
30431         /**
30432          * Button config that displays a single OK button
30433          * @type Object
30434          */
30435         OK : {ok:true},
30436         /**
30437          * Button config that displays Yes and No buttons
30438          * @type Object
30439          */
30440         YESNO : {yes:true, no:true},
30441         /**
30442          * Button config that displays OK and Cancel buttons
30443          * @type Object
30444          */
30445         OKCANCEL : {ok:true, cancel:true},
30446         /**
30447          * Button config that displays Yes, No and Cancel buttons
30448          * @type Object
30449          */
30450         YESNOCANCEL : {yes:true, no:true, cancel:true},
30451
30452         /**
30453          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30454          * @type Number
30455          */
30456         defaultTextHeight : 75,
30457         /**
30458          * The maximum width in pixels of the message box (defaults to 600)
30459          * @type Number
30460          */
30461         maxWidth : 600,
30462         /**
30463          * The minimum width in pixels of the message box (defaults to 100)
30464          * @type Number
30465          */
30466         minWidth : 100,
30467         /**
30468          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30469          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30470          * @type Number
30471          */
30472         minProgressWidth : 250,
30473         /**
30474          * An object containing the default button text strings that can be overriden for localized language support.
30475          * Supported properties are: ok, cancel, yes and no.
30476          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30477          * @type Object
30478          */
30479         buttonText : {
30480             ok : "OK",
30481             cancel : "Cancel",
30482             yes : "Yes",
30483             no : "No"
30484         }
30485     };
30486 }();
30487
30488 /**
30489  * Shorthand for {@link Roo.MessageBox}
30490  */
30491 Roo.Msg = Roo.MessageBox;/*
30492  * Based on:
30493  * Ext JS Library 1.1.1
30494  * Copyright(c) 2006-2007, Ext JS, LLC.
30495  *
30496  * Originally Released Under LGPL - original licence link has changed is not relivant.
30497  *
30498  * Fork - LGPL
30499  * <script type="text/javascript">
30500  */
30501 /**
30502  * @class Roo.QuickTips
30503  * Provides attractive and customizable tooltips for any element.
30504  * @singleton
30505  */
30506 Roo.QuickTips = function(){
30507     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30508     var ce, bd, xy, dd;
30509     var visible = false, disabled = true, inited = false;
30510     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30511     
30512     var onOver = function(e){
30513         if(disabled){
30514             return;
30515         }
30516         var t = e.getTarget();
30517         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30518             return;
30519         }
30520         if(ce && t == ce.el){
30521             clearTimeout(hideProc);
30522             return;
30523         }
30524         if(t && tagEls[t.id]){
30525             tagEls[t.id].el = t;
30526             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30527             return;
30528         }
30529         var ttp, et = Roo.fly(t);
30530         var ns = cfg.namespace;
30531         if(tm.interceptTitles && t.title){
30532             ttp = t.title;
30533             t.qtip = ttp;
30534             t.removeAttribute("title");
30535             e.preventDefault();
30536         }else{
30537             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30538         }
30539         if(ttp){
30540             showProc = show.defer(tm.showDelay, tm, [{
30541                 el: t, 
30542                 text: ttp, 
30543                 width: et.getAttributeNS(ns, cfg.width),
30544                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30545                 title: et.getAttributeNS(ns, cfg.title),
30546                     cls: et.getAttributeNS(ns, cfg.cls)
30547             }]);
30548         }
30549     };
30550     
30551     var onOut = function(e){
30552         clearTimeout(showProc);
30553         var t = e.getTarget();
30554         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30555             hideProc = setTimeout(hide, tm.hideDelay);
30556         }
30557     };
30558     
30559     var onMove = function(e){
30560         if(disabled){
30561             return;
30562         }
30563         xy = e.getXY();
30564         xy[1] += 18;
30565         if(tm.trackMouse && ce){
30566             el.setXY(xy);
30567         }
30568     };
30569     
30570     var onDown = function(e){
30571         clearTimeout(showProc);
30572         clearTimeout(hideProc);
30573         if(!e.within(el)){
30574             if(tm.hideOnClick){
30575                 hide();
30576                 tm.disable();
30577                 tm.enable.defer(100, tm);
30578             }
30579         }
30580     };
30581     
30582     var getPad = function(){
30583         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30584     };
30585
30586     var show = function(o){
30587         if(disabled){
30588             return;
30589         }
30590         clearTimeout(dismissProc);
30591         ce = o;
30592         if(removeCls){ // in case manually hidden
30593             el.removeClass(removeCls);
30594             removeCls = null;
30595         }
30596         if(ce.cls){
30597             el.addClass(ce.cls);
30598             removeCls = ce.cls;
30599         }
30600         if(ce.title){
30601             tipTitle.update(ce.title);
30602             tipTitle.show();
30603         }else{
30604             tipTitle.update('');
30605             tipTitle.hide();
30606         }
30607         el.dom.style.width  = tm.maxWidth+'px';
30608         //tipBody.dom.style.width = '';
30609         tipBodyText.update(o.text);
30610         var p = getPad(), w = ce.width;
30611         if(!w){
30612             var td = tipBodyText.dom;
30613             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30614             if(aw > tm.maxWidth){
30615                 w = tm.maxWidth;
30616             }else if(aw < tm.minWidth){
30617                 w = tm.minWidth;
30618             }else{
30619                 w = aw;
30620             }
30621         }
30622         //tipBody.setWidth(w);
30623         el.setWidth(parseInt(w, 10) + p);
30624         if(ce.autoHide === false){
30625             close.setDisplayed(true);
30626             if(dd){
30627                 dd.unlock();
30628             }
30629         }else{
30630             close.setDisplayed(false);
30631             if(dd){
30632                 dd.lock();
30633             }
30634         }
30635         if(xy){
30636             el.avoidY = xy[1]-18;
30637             el.setXY(xy);
30638         }
30639         if(tm.animate){
30640             el.setOpacity(.1);
30641             el.setStyle("visibility", "visible");
30642             el.fadeIn({callback: afterShow});
30643         }else{
30644             afterShow();
30645         }
30646     };
30647     
30648     var afterShow = function(){
30649         if(ce){
30650             el.show();
30651             esc.enable();
30652             if(tm.autoDismiss && ce.autoHide !== false){
30653                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30654             }
30655         }
30656     };
30657     
30658     var hide = function(noanim){
30659         clearTimeout(dismissProc);
30660         clearTimeout(hideProc);
30661         ce = null;
30662         if(el.isVisible()){
30663             esc.disable();
30664             if(noanim !== true && tm.animate){
30665                 el.fadeOut({callback: afterHide});
30666             }else{
30667                 afterHide();
30668             } 
30669         }
30670     };
30671     
30672     var afterHide = function(){
30673         el.hide();
30674         if(removeCls){
30675             el.removeClass(removeCls);
30676             removeCls = null;
30677         }
30678     };
30679     
30680     return {
30681         /**
30682         * @cfg {Number} minWidth
30683         * The minimum width of the quick tip (defaults to 40)
30684         */
30685        minWidth : 40,
30686         /**
30687         * @cfg {Number} maxWidth
30688         * The maximum width of the quick tip (defaults to 300)
30689         */
30690        maxWidth : 300,
30691         /**
30692         * @cfg {Boolean} interceptTitles
30693         * True to automatically use the element's DOM title value if available (defaults to false)
30694         */
30695        interceptTitles : false,
30696         /**
30697         * @cfg {Boolean} trackMouse
30698         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30699         */
30700        trackMouse : false,
30701         /**
30702         * @cfg {Boolean} hideOnClick
30703         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30704         */
30705        hideOnClick : true,
30706         /**
30707         * @cfg {Number} showDelay
30708         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30709         */
30710        showDelay : 500,
30711         /**
30712         * @cfg {Number} hideDelay
30713         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30714         */
30715        hideDelay : 200,
30716         /**
30717         * @cfg {Boolean} autoHide
30718         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30719         * Used in conjunction with hideDelay.
30720         */
30721        autoHide : true,
30722         /**
30723         * @cfg {Boolean}
30724         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30725         * (defaults to true).  Used in conjunction with autoDismissDelay.
30726         */
30727        autoDismiss : true,
30728         /**
30729         * @cfg {Number}
30730         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30731         */
30732        autoDismissDelay : 5000,
30733        /**
30734         * @cfg {Boolean} animate
30735         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30736         */
30737        animate : false,
30738
30739        /**
30740         * @cfg {String} title
30741         * Title text to display (defaults to '').  This can be any valid HTML markup.
30742         */
30743         title: '',
30744        /**
30745         * @cfg {String} text
30746         * Body text to display (defaults to '').  This can be any valid HTML markup.
30747         */
30748         text : '',
30749        /**
30750         * @cfg {String} cls
30751         * A CSS class to apply to the base quick tip element (defaults to '').
30752         */
30753         cls : '',
30754        /**
30755         * @cfg {Number} width
30756         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30757         * minWidth or maxWidth.
30758         */
30759         width : null,
30760
30761     /**
30762      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30763      * or display QuickTips in a page.
30764      */
30765        init : function(){
30766           tm = Roo.QuickTips;
30767           cfg = tm.tagConfig;
30768           if(!inited){
30769               if(!Roo.isReady){ // allow calling of init() before onReady
30770                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30771                   return;
30772               }
30773               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30774               el.fxDefaults = {stopFx: true};
30775               // maximum custom styling
30776               //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>');
30777               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>');              
30778               tipTitle = el.child('h3');
30779               tipTitle.enableDisplayMode("block");
30780               tipBody = el.child('div.x-tip-bd');
30781               tipBodyText = el.child('div.x-tip-bd-inner');
30782               //bdLeft = el.child('div.x-tip-bd-left');
30783               //bdRight = el.child('div.x-tip-bd-right');
30784               close = el.child('div.x-tip-close');
30785               close.enableDisplayMode("block");
30786               close.on("click", hide);
30787               var d = Roo.get(document);
30788               d.on("mousedown", onDown);
30789               d.on("mouseover", onOver);
30790               d.on("mouseout", onOut);
30791               d.on("mousemove", onMove);
30792               esc = d.addKeyListener(27, hide);
30793               esc.disable();
30794               if(Roo.dd.DD){
30795                   dd = el.initDD("default", null, {
30796                       onDrag : function(){
30797                           el.sync();  
30798                       }
30799                   });
30800                   dd.setHandleElId(tipTitle.id);
30801                   dd.lock();
30802               }
30803               inited = true;
30804           }
30805           this.enable(); 
30806        },
30807
30808     /**
30809      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30810      * are supported:
30811      * <pre>
30812 Property    Type                   Description
30813 ----------  ---------------------  ------------------------------------------------------------------------
30814 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30815      * </ul>
30816      * @param {Object} config The config object
30817      */
30818        register : function(config){
30819            var cs = config instanceof Array ? config : arguments;
30820            for(var i = 0, len = cs.length; i < len; i++) {
30821                var c = cs[i];
30822                var target = c.target;
30823                if(target){
30824                    if(target instanceof Array){
30825                        for(var j = 0, jlen = target.length; j < jlen; j++){
30826                            tagEls[target[j]] = c;
30827                        }
30828                    }else{
30829                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30830                    }
30831                }
30832            }
30833        },
30834
30835     /**
30836      * Removes this quick tip from its element and destroys it.
30837      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30838      */
30839        unregister : function(el){
30840            delete tagEls[Roo.id(el)];
30841        },
30842
30843     /**
30844      * Enable this quick tip.
30845      */
30846        enable : function(){
30847            if(inited && disabled){
30848                locks.pop();
30849                if(locks.length < 1){
30850                    disabled = false;
30851                }
30852            }
30853        },
30854
30855     /**
30856      * Disable this quick tip.
30857      */
30858        disable : function(){
30859           disabled = true;
30860           clearTimeout(showProc);
30861           clearTimeout(hideProc);
30862           clearTimeout(dismissProc);
30863           if(ce){
30864               hide(true);
30865           }
30866           locks.push(1);
30867        },
30868
30869     /**
30870      * Returns true if the quick tip is enabled, else false.
30871      */
30872        isEnabled : function(){
30873             return !disabled;
30874        },
30875
30876         // private
30877        tagConfig : {
30878            namespace : "ext",
30879            attribute : "qtip",
30880            width : "width",
30881            target : "target",
30882            title : "qtitle",
30883            hide : "hide",
30884            cls : "qclass"
30885        }
30886    };
30887 }();
30888
30889 // backwards compat
30890 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30891  * Based on:
30892  * Ext JS Library 1.1.1
30893  * Copyright(c) 2006-2007, Ext JS, LLC.
30894  *
30895  * Originally Released Under LGPL - original licence link has changed is not relivant.
30896  *
30897  * Fork - LGPL
30898  * <script type="text/javascript">
30899  */
30900  
30901
30902 /**
30903  * @class Roo.tree.TreePanel
30904  * @extends Roo.data.Tree
30905
30906  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30907  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30908  * @cfg {Boolean} enableDD true to enable drag and drop
30909  * @cfg {Boolean} enableDrag true to enable just drag
30910  * @cfg {Boolean} enableDrop true to enable just drop
30911  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30912  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30913  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30914  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30915  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30916  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30917  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30918  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30919  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30920  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30921  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30922  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30923  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
30924  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30925  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30926  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30927  * 
30928  * @constructor
30929  * @param {String/HTMLElement/Element} el The container element
30930  * @param {Object} config
30931  */
30932 Roo.tree.TreePanel = function(el, config){
30933     var root = false;
30934     var loader = false;
30935     if (config.root) {
30936         root = config.root;
30937         delete config.root;
30938     }
30939     if (config.loader) {
30940         loader = config.loader;
30941         delete config.loader;
30942     }
30943     
30944     Roo.apply(this, config);
30945     Roo.tree.TreePanel.superclass.constructor.call(this);
30946     this.el = Roo.get(el);
30947     this.el.addClass('x-tree');
30948     //console.log(root);
30949     if (root) {
30950         this.setRootNode( Roo.factory(root, Roo.tree));
30951     }
30952     if (loader) {
30953         this.loader = Roo.factory(loader, Roo.tree);
30954     }
30955    /**
30956     * Read-only. The id of the container element becomes this TreePanel's id.
30957     */
30958     this.id = this.el.id;
30959     this.addEvents({
30960         /**
30961         * @event beforeload
30962         * Fires before a node is loaded, return false to cancel
30963         * @param {Node} node The node being loaded
30964         */
30965         "beforeload" : true,
30966         /**
30967         * @event load
30968         * Fires when a node is loaded
30969         * @param {Node} node The node that was loaded
30970         */
30971         "load" : true,
30972         /**
30973         * @event textchange
30974         * Fires when the text for a node is changed
30975         * @param {Node} node The node
30976         * @param {String} text The new text
30977         * @param {String} oldText The old text
30978         */
30979         "textchange" : true,
30980         /**
30981         * @event beforeexpand
30982         * Fires before a node is expanded, return false to cancel.
30983         * @param {Node} node The node
30984         * @param {Boolean} deep
30985         * @param {Boolean} anim
30986         */
30987         "beforeexpand" : true,
30988         /**
30989         * @event beforecollapse
30990         * Fires before a node is collapsed, return false to cancel.
30991         * @param {Node} node The node
30992         * @param {Boolean} deep
30993         * @param {Boolean} anim
30994         */
30995         "beforecollapse" : true,
30996         /**
30997         * @event expand
30998         * Fires when a node is expanded
30999         * @param {Node} node The node
31000         */
31001         "expand" : true,
31002         /**
31003         * @event disabledchange
31004         * Fires when the disabled status of a node changes
31005         * @param {Node} node The node
31006         * @param {Boolean} disabled
31007         */
31008         "disabledchange" : true,
31009         /**
31010         * @event collapse
31011         * Fires when a node is collapsed
31012         * @param {Node} node The node
31013         */
31014         "collapse" : true,
31015         /**
31016         * @event beforeclick
31017         * Fires before click processing on a node. Return false to cancel the default action.
31018         * @param {Node} node The node
31019         * @param {Roo.EventObject} e The event object
31020         */
31021         "beforeclick":true,
31022         /**
31023         * @event checkchange
31024         * Fires when a node with a checkbox's checked property changes
31025         * @param {Node} this This node
31026         * @param {Boolean} checked
31027         */
31028         "checkchange":true,
31029         /**
31030         * @event click
31031         * Fires when a node is clicked
31032         * @param {Node} node The node
31033         * @param {Roo.EventObject} e The event object
31034         */
31035         "click":true,
31036         /**
31037         * @event dblclick
31038         * Fires when a node is double clicked
31039         * @param {Node} node The node
31040         * @param {Roo.EventObject} e The event object
31041         */
31042         "dblclick":true,
31043         /**
31044         * @event contextmenu
31045         * Fires when a node is right clicked
31046         * @param {Node} node The node
31047         * @param {Roo.EventObject} e The event object
31048         */
31049         "contextmenu":true,
31050         /**
31051         * @event beforechildrenrendered
31052         * Fires right before the child nodes for a node are rendered
31053         * @param {Node} node The node
31054         */
31055         "beforechildrenrendered":true,
31056         /**
31057         * @event startdrag
31058         * Fires when a node starts being dragged
31059         * @param {Roo.tree.TreePanel} this
31060         * @param {Roo.tree.TreeNode} node
31061         * @param {event} e The raw browser event
31062         */ 
31063        "startdrag" : true,
31064        /**
31065         * @event enddrag
31066         * Fires when a drag operation is complete
31067         * @param {Roo.tree.TreePanel} this
31068         * @param {Roo.tree.TreeNode} node
31069         * @param {event} e The raw browser event
31070         */
31071        "enddrag" : true,
31072        /**
31073         * @event dragdrop
31074         * Fires when a dragged node is dropped on a valid DD target
31075         * @param {Roo.tree.TreePanel} this
31076         * @param {Roo.tree.TreeNode} node
31077         * @param {DD} dd The dd it was dropped on
31078         * @param {event} e The raw browser event
31079         */
31080        "dragdrop" : true,
31081        /**
31082         * @event beforenodedrop
31083         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
31084         * passed to handlers has the following properties:<br />
31085         * <ul style="padding:5px;padding-left:16px;">
31086         * <li>tree - The TreePanel</li>
31087         * <li>target - The node being targeted for the drop</li>
31088         * <li>data - The drag data from the drag source</li>
31089         * <li>point - The point of the drop - append, above or below</li>
31090         * <li>source - The drag source</li>
31091         * <li>rawEvent - Raw mouse event</li>
31092         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
31093         * to be inserted by setting them on this object.</li>
31094         * <li>cancel - Set this to true to cancel the drop.</li>
31095         * </ul>
31096         * @param {Object} dropEvent
31097         */
31098        "beforenodedrop" : true,
31099        /**
31100         * @event nodedrop
31101         * Fires after a DD object is dropped on a node in this tree. The dropEvent
31102         * passed to handlers has the following properties:<br />
31103         * <ul style="padding:5px;padding-left:16px;">
31104         * <li>tree - The TreePanel</li>
31105         * <li>target - The node being targeted for the drop</li>
31106         * <li>data - The drag data from the drag source</li>
31107         * <li>point - The point of the drop - append, above or below</li>
31108         * <li>source - The drag source</li>
31109         * <li>rawEvent - Raw mouse event</li>
31110         * <li>dropNode - Dropped node(s).</li>
31111         * </ul>
31112         * @param {Object} dropEvent
31113         */
31114        "nodedrop" : true,
31115         /**
31116         * @event nodedragover
31117         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
31118         * passed to handlers has the following properties:<br />
31119         * <ul style="padding:5px;padding-left:16px;">
31120         * <li>tree - The TreePanel</li>
31121         * <li>target - The node being targeted for the drop</li>
31122         * <li>data - The drag data from the drag source</li>
31123         * <li>point - The point of the drop - append, above or below</li>
31124         * <li>source - The drag source</li>
31125         * <li>rawEvent - Raw mouse event</li>
31126         * <li>dropNode - Drop node(s) provided by the source.</li>
31127         * <li>cancel - Set this to true to signal drop not allowed.</li>
31128         * </ul>
31129         * @param {Object} dragOverEvent
31130         */
31131        "nodedragover" : true
31132         
31133     });
31134     if(this.singleExpand){
31135        this.on("beforeexpand", this.restrictExpand, this);
31136     }
31137     if (this.editor) {
31138         this.editor.tree = this;
31139         this.editor = Roo.factory(this.editor, Roo.tree);
31140     }
31141     
31142     if (this.selModel) {
31143         this.selModel = Roo.factory(this.selModel, Roo.tree);
31144     }
31145    
31146 };
31147 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
31148     rootVisible : true,
31149     animate: Roo.enableFx,
31150     lines : true,
31151     enableDD : false,
31152     hlDrop : Roo.enableFx,
31153   
31154     renderer: false,
31155     
31156     rendererTip: false,
31157     // private
31158     restrictExpand : function(node){
31159         var p = node.parentNode;
31160         if(p){
31161             if(p.expandedChild && p.expandedChild.parentNode == p){
31162                 p.expandedChild.collapse();
31163             }
31164             p.expandedChild = node;
31165         }
31166     },
31167
31168     // private override
31169     setRootNode : function(node){
31170         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
31171         if(!this.rootVisible){
31172             node.ui = new Roo.tree.RootTreeNodeUI(node);
31173         }
31174         return node;
31175     },
31176
31177     /**
31178      * Returns the container element for this TreePanel
31179      */
31180     getEl : function(){
31181         return this.el;
31182     },
31183
31184     /**
31185      * Returns the default TreeLoader for this TreePanel
31186      */
31187     getLoader : function(){
31188         return this.loader;
31189     },
31190
31191     /**
31192      * Expand all nodes
31193      */
31194     expandAll : function(){
31195         this.root.expand(true);
31196     },
31197
31198     /**
31199      * Collapse all nodes
31200      */
31201     collapseAll : function(){
31202         this.root.collapse(true);
31203     },
31204
31205     /**
31206      * Returns the selection model used by this TreePanel
31207      */
31208     getSelectionModel : function(){
31209         if(!this.selModel){
31210             this.selModel = new Roo.tree.DefaultSelectionModel();
31211         }
31212         return this.selModel;
31213     },
31214
31215     /**
31216      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
31217      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
31218      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
31219      * @return {Array}
31220      */
31221     getChecked : function(a, startNode){
31222         startNode = startNode || this.root;
31223         var r = [];
31224         var f = function(){
31225             if(this.attributes.checked){
31226                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
31227             }
31228         }
31229         startNode.cascade(f);
31230         return r;
31231     },
31232
31233     /**
31234      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31235      * @param {String} path
31236      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31237      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
31238      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
31239      */
31240     expandPath : function(path, attr, callback){
31241         attr = attr || "id";
31242         var keys = path.split(this.pathSeparator);
31243         var curNode = this.root;
31244         if(curNode.attributes[attr] != keys[1]){ // invalid root
31245             if(callback){
31246                 callback(false, null);
31247             }
31248             return;
31249         }
31250         var index = 1;
31251         var f = function(){
31252             if(++index == keys.length){
31253                 if(callback){
31254                     callback(true, curNode);
31255                 }
31256                 return;
31257             }
31258             var c = curNode.findChild(attr, keys[index]);
31259             if(!c){
31260                 if(callback){
31261                     callback(false, curNode);
31262                 }
31263                 return;
31264             }
31265             curNode = c;
31266             c.expand(false, false, f);
31267         };
31268         curNode.expand(false, false, f);
31269     },
31270
31271     /**
31272      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31273      * @param {String} path
31274      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31275      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31276      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31277      */
31278     selectPath : function(path, attr, callback){
31279         attr = attr || "id";
31280         var keys = path.split(this.pathSeparator);
31281         var v = keys.pop();
31282         if(keys.length > 0){
31283             var f = function(success, node){
31284                 if(success && node){
31285                     var n = node.findChild(attr, v);
31286                     if(n){
31287                         n.select();
31288                         if(callback){
31289                             callback(true, n);
31290                         }
31291                     }else if(callback){
31292                         callback(false, n);
31293                     }
31294                 }else{
31295                     if(callback){
31296                         callback(false, n);
31297                     }
31298                 }
31299             };
31300             this.expandPath(keys.join(this.pathSeparator), attr, f);
31301         }else{
31302             this.root.select();
31303             if(callback){
31304                 callback(true, this.root);
31305             }
31306         }
31307     },
31308
31309     getTreeEl : function(){
31310         return this.el;
31311     },
31312
31313     /**
31314      * Trigger rendering of this TreePanel
31315      */
31316     render : function(){
31317         if (this.innerCt) {
31318             return this; // stop it rendering more than once!!
31319         }
31320         
31321         this.innerCt = this.el.createChild({tag:"ul",
31322                cls:"x-tree-root-ct " +
31323                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31324
31325         if(this.containerScroll){
31326             Roo.dd.ScrollManager.register(this.el);
31327         }
31328         if((this.enableDD || this.enableDrop) && !this.dropZone){
31329            /**
31330             * The dropZone used by this tree if drop is enabled
31331             * @type Roo.tree.TreeDropZone
31332             */
31333              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31334                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31335            });
31336         }
31337         if((this.enableDD || this.enableDrag) && !this.dragZone){
31338            /**
31339             * The dragZone used by this tree if drag is enabled
31340             * @type Roo.tree.TreeDragZone
31341             */
31342             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31343                ddGroup: this.ddGroup || "TreeDD",
31344                scroll: this.ddScroll
31345            });
31346         }
31347         this.getSelectionModel().init(this);
31348         if (!this.root) {
31349             console.log("ROOT not set in tree");
31350             return;
31351         }
31352         this.root.render();
31353         if(!this.rootVisible){
31354             this.root.renderChildren();
31355         }
31356         return this;
31357     }
31358 });/*
31359  * Based on:
31360  * Ext JS Library 1.1.1
31361  * Copyright(c) 2006-2007, Ext JS, LLC.
31362  *
31363  * Originally Released Under LGPL - original licence link has changed is not relivant.
31364  *
31365  * Fork - LGPL
31366  * <script type="text/javascript">
31367  */
31368  
31369
31370 /**
31371  * @class Roo.tree.DefaultSelectionModel
31372  * @extends Roo.util.Observable
31373  * The default single selection for a TreePanel.
31374  * @param {Object} cfg Configuration
31375  */
31376 Roo.tree.DefaultSelectionModel = function(cfg){
31377    this.selNode = null;
31378    
31379    
31380    
31381    this.addEvents({
31382        /**
31383         * @event selectionchange
31384         * Fires when the selected node changes
31385         * @param {DefaultSelectionModel} this
31386         * @param {TreeNode} node the new selection
31387         */
31388        "selectionchange" : true,
31389
31390        /**
31391         * @event beforeselect
31392         * Fires before the selected node changes, return false to cancel the change
31393         * @param {DefaultSelectionModel} this
31394         * @param {TreeNode} node the new selection
31395         * @param {TreeNode} node the old selection
31396         */
31397        "beforeselect" : true
31398    });
31399    
31400     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
31401 };
31402
31403 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31404     init : function(tree){
31405         this.tree = tree;
31406         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31407         tree.on("click", this.onNodeClick, this);
31408     },
31409     
31410     onNodeClick : function(node, e){
31411         if (e.ctrlKey && this.selNode == node)  {
31412             this.unselect(node);
31413             return;
31414         }
31415         this.select(node);
31416     },
31417     
31418     /**
31419      * Select a node.
31420      * @param {TreeNode} node The node to select
31421      * @return {TreeNode} The selected node
31422      */
31423     select : function(node){
31424         var last = this.selNode;
31425         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31426             if(last){
31427                 last.ui.onSelectedChange(false);
31428             }
31429             this.selNode = node;
31430             node.ui.onSelectedChange(true);
31431             this.fireEvent("selectionchange", this, node, last);
31432         }
31433         return node;
31434     },
31435     
31436     /**
31437      * Deselect a node.
31438      * @param {TreeNode} node The node to unselect
31439      */
31440     unselect : function(node){
31441         if(this.selNode == node){
31442             this.clearSelections();
31443         }    
31444     },
31445     
31446     /**
31447      * Clear all selections
31448      */
31449     clearSelections : function(){
31450         var n = this.selNode;
31451         if(n){
31452             n.ui.onSelectedChange(false);
31453             this.selNode = null;
31454             this.fireEvent("selectionchange", this, null);
31455         }
31456         return n;
31457     },
31458     
31459     /**
31460      * Get the selected node
31461      * @return {TreeNode} The selected node
31462      */
31463     getSelectedNode : function(){
31464         return this.selNode;    
31465     },
31466     
31467     /**
31468      * Returns true if the node is selected
31469      * @param {TreeNode} node The node to check
31470      * @return {Boolean}
31471      */
31472     isSelected : function(node){
31473         return this.selNode == node;  
31474     },
31475
31476     /**
31477      * Selects the node above the selected node in the tree, intelligently walking the nodes
31478      * @return TreeNode The new selection
31479      */
31480     selectPrevious : function(){
31481         var s = this.selNode || this.lastSelNode;
31482         if(!s){
31483             return null;
31484         }
31485         var ps = s.previousSibling;
31486         if(ps){
31487             if(!ps.isExpanded() || ps.childNodes.length < 1){
31488                 return this.select(ps);
31489             } else{
31490                 var lc = ps.lastChild;
31491                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31492                     lc = lc.lastChild;
31493                 }
31494                 return this.select(lc);
31495             }
31496         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31497             return this.select(s.parentNode);
31498         }
31499         return null;
31500     },
31501
31502     /**
31503      * Selects the node above the selected node in the tree, intelligently walking the nodes
31504      * @return TreeNode The new selection
31505      */
31506     selectNext : function(){
31507         var s = this.selNode || this.lastSelNode;
31508         if(!s){
31509             return null;
31510         }
31511         if(s.firstChild && s.isExpanded()){
31512              return this.select(s.firstChild);
31513          }else if(s.nextSibling){
31514              return this.select(s.nextSibling);
31515          }else if(s.parentNode){
31516             var newS = null;
31517             s.parentNode.bubble(function(){
31518                 if(this.nextSibling){
31519                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31520                     return false;
31521                 }
31522             });
31523             return newS;
31524          }
31525         return null;
31526     },
31527
31528     onKeyDown : function(e){
31529         var s = this.selNode || this.lastSelNode;
31530         // undesirable, but required
31531         var sm = this;
31532         if(!s){
31533             return;
31534         }
31535         var k = e.getKey();
31536         switch(k){
31537              case e.DOWN:
31538                  e.stopEvent();
31539                  this.selectNext();
31540              break;
31541              case e.UP:
31542                  e.stopEvent();
31543                  this.selectPrevious();
31544              break;
31545              case e.RIGHT:
31546                  e.preventDefault();
31547                  if(s.hasChildNodes()){
31548                      if(!s.isExpanded()){
31549                          s.expand();
31550                      }else if(s.firstChild){
31551                          this.select(s.firstChild, e);
31552                      }
31553                  }
31554              break;
31555              case e.LEFT:
31556                  e.preventDefault();
31557                  if(s.hasChildNodes() && s.isExpanded()){
31558                      s.collapse();
31559                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31560                      this.select(s.parentNode, e);
31561                  }
31562              break;
31563         };
31564     }
31565 });
31566
31567 /**
31568  * @class Roo.tree.MultiSelectionModel
31569  * @extends Roo.util.Observable
31570  * Multi selection for a TreePanel.
31571  * @param {Object} cfg Configuration
31572  */
31573 Roo.tree.MultiSelectionModel = function(){
31574    this.selNodes = [];
31575    this.selMap = {};
31576    this.addEvents({
31577        /**
31578         * @event selectionchange
31579         * Fires when the selected nodes change
31580         * @param {MultiSelectionModel} this
31581         * @param {Array} nodes Array of the selected nodes
31582         */
31583        "selectionchange" : true
31584    });
31585    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
31586    
31587 };
31588
31589 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31590     init : function(tree){
31591         this.tree = tree;
31592         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31593         tree.on("click", this.onNodeClick, this);
31594     },
31595     
31596     onNodeClick : function(node, e){
31597         this.select(node, e, e.ctrlKey);
31598     },
31599     
31600     /**
31601      * Select a node.
31602      * @param {TreeNode} node The node to select
31603      * @param {EventObject} e (optional) An event associated with the selection
31604      * @param {Boolean} keepExisting True to retain existing selections
31605      * @return {TreeNode} The selected node
31606      */
31607     select : function(node, e, keepExisting){
31608         if(keepExisting !== true){
31609             this.clearSelections(true);
31610         }
31611         if(this.isSelected(node)){
31612             this.lastSelNode = node;
31613             return node;
31614         }
31615         this.selNodes.push(node);
31616         this.selMap[node.id] = node;
31617         this.lastSelNode = node;
31618         node.ui.onSelectedChange(true);
31619         this.fireEvent("selectionchange", this, this.selNodes);
31620         return node;
31621     },
31622     
31623     /**
31624      * Deselect a node.
31625      * @param {TreeNode} node The node to unselect
31626      */
31627     unselect : function(node){
31628         if(this.selMap[node.id]){
31629             node.ui.onSelectedChange(false);
31630             var sn = this.selNodes;
31631             var index = -1;
31632             if(sn.indexOf){
31633                 index = sn.indexOf(node);
31634             }else{
31635                 for(var i = 0, len = sn.length; i < len; i++){
31636                     if(sn[i] == node){
31637                         index = i;
31638                         break;
31639                     }
31640                 }
31641             }
31642             if(index != -1){
31643                 this.selNodes.splice(index, 1);
31644             }
31645             delete this.selMap[node.id];
31646             this.fireEvent("selectionchange", this, this.selNodes);
31647         }
31648     },
31649     
31650     /**
31651      * Clear all selections
31652      */
31653     clearSelections : function(suppressEvent){
31654         var sn = this.selNodes;
31655         if(sn.length > 0){
31656             for(var i = 0, len = sn.length; i < len; i++){
31657                 sn[i].ui.onSelectedChange(false);
31658             }
31659             this.selNodes = [];
31660             this.selMap = {};
31661             if(suppressEvent !== true){
31662                 this.fireEvent("selectionchange", this, this.selNodes);
31663             }
31664         }
31665     },
31666     
31667     /**
31668      * Returns true if the node is selected
31669      * @param {TreeNode} node The node to check
31670      * @return {Boolean}
31671      */
31672     isSelected : function(node){
31673         return this.selMap[node.id] ? true : false;  
31674     },
31675     
31676     /**
31677      * Returns an array of the selected nodes
31678      * @return {Array}
31679      */
31680     getSelectedNodes : function(){
31681         return this.selNodes;    
31682     },
31683
31684     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31685
31686     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31687
31688     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31689 });/*
31690  * Based on:
31691  * Ext JS Library 1.1.1
31692  * Copyright(c) 2006-2007, Ext JS, LLC.
31693  *
31694  * Originally Released Under LGPL - original licence link has changed is not relivant.
31695  *
31696  * Fork - LGPL
31697  * <script type="text/javascript">
31698  */
31699  
31700 /**
31701  * @class Roo.tree.TreeNode
31702  * @extends Roo.data.Node
31703  * @cfg {String} text The text for this node
31704  * @cfg {Boolean} expanded true to start the node expanded
31705  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31706  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31707  * @cfg {Boolean} disabled true to start the node disabled
31708  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31709  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31710  * @cfg {String} cls A css class to be added to the node
31711  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31712  * @cfg {String} href URL of the link used for the node (defaults to #)
31713  * @cfg {String} hrefTarget target frame for the link
31714  * @cfg {String} qtip An Ext QuickTip for the node
31715  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31716  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31717  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31718  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31719  * (defaults to undefined with no checkbox rendered)
31720  * @constructor
31721  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31722  */
31723 Roo.tree.TreeNode = function(attributes){
31724     attributes = attributes || {};
31725     if(typeof attributes == "string"){
31726         attributes = {text: attributes};
31727     }
31728     this.childrenRendered = false;
31729     this.rendered = false;
31730     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31731     this.expanded = attributes.expanded === true;
31732     this.isTarget = attributes.isTarget !== false;
31733     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31734     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31735
31736     /**
31737      * Read-only. The text for this node. To change it use setText().
31738      * @type String
31739      */
31740     this.text = attributes.text;
31741     /**
31742      * True if this node is disabled.
31743      * @type Boolean
31744      */
31745     this.disabled = attributes.disabled === true;
31746
31747     this.addEvents({
31748         /**
31749         * @event textchange
31750         * Fires when the text for this node is changed
31751         * @param {Node} this This node
31752         * @param {String} text The new text
31753         * @param {String} oldText The old text
31754         */
31755         "textchange" : true,
31756         /**
31757         * @event beforeexpand
31758         * Fires before this node is expanded, return false to cancel.
31759         * @param {Node} this This node
31760         * @param {Boolean} deep
31761         * @param {Boolean} anim
31762         */
31763         "beforeexpand" : true,
31764         /**
31765         * @event beforecollapse
31766         * Fires before this node is collapsed, return false to cancel.
31767         * @param {Node} this This node
31768         * @param {Boolean} deep
31769         * @param {Boolean} anim
31770         */
31771         "beforecollapse" : true,
31772         /**
31773         * @event expand
31774         * Fires when this node is expanded
31775         * @param {Node} this This node
31776         */
31777         "expand" : true,
31778         /**
31779         * @event disabledchange
31780         * Fires when the disabled status of this node changes
31781         * @param {Node} this This node
31782         * @param {Boolean} disabled
31783         */
31784         "disabledchange" : true,
31785         /**
31786         * @event collapse
31787         * Fires when this node is collapsed
31788         * @param {Node} this This node
31789         */
31790         "collapse" : true,
31791         /**
31792         * @event beforeclick
31793         * Fires before click processing. Return false to cancel the default action.
31794         * @param {Node} this This node
31795         * @param {Roo.EventObject} e The event object
31796         */
31797         "beforeclick":true,
31798         /**
31799         * @event checkchange
31800         * Fires when a node with a checkbox's checked property changes
31801         * @param {Node} this This node
31802         * @param {Boolean} checked
31803         */
31804         "checkchange":true,
31805         /**
31806         * @event click
31807         * Fires when this node is clicked
31808         * @param {Node} this This node
31809         * @param {Roo.EventObject} e The event object
31810         */
31811         "click":true,
31812         /**
31813         * @event dblclick
31814         * Fires when this node is double clicked
31815         * @param {Node} this This node
31816         * @param {Roo.EventObject} e The event object
31817         */
31818         "dblclick":true,
31819         /**
31820         * @event contextmenu
31821         * Fires when this node is right clicked
31822         * @param {Node} this This node
31823         * @param {Roo.EventObject} e The event object
31824         */
31825         "contextmenu":true,
31826         /**
31827         * @event beforechildrenrendered
31828         * Fires right before the child nodes for this node are rendered
31829         * @param {Node} this This node
31830         */
31831         "beforechildrenrendered":true
31832     });
31833
31834     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31835
31836     /**
31837      * Read-only. The UI for this node
31838      * @type TreeNodeUI
31839      */
31840     this.ui = new uiClass(this);
31841 };
31842 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31843     preventHScroll: true,
31844     /**
31845      * Returns true if this node is expanded
31846      * @return {Boolean}
31847      */
31848     isExpanded : function(){
31849         return this.expanded;
31850     },
31851
31852     /**
31853      * Returns the UI object for this node
31854      * @return {TreeNodeUI}
31855      */
31856     getUI : function(){
31857         return this.ui;
31858     },
31859
31860     // private override
31861     setFirstChild : function(node){
31862         var of = this.firstChild;
31863         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31864         if(this.childrenRendered && of && node != of){
31865             of.renderIndent(true, true);
31866         }
31867         if(this.rendered){
31868             this.renderIndent(true, true);
31869         }
31870     },
31871
31872     // private override
31873     setLastChild : function(node){
31874         var ol = this.lastChild;
31875         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31876         if(this.childrenRendered && ol && node != ol){
31877             ol.renderIndent(true, true);
31878         }
31879         if(this.rendered){
31880             this.renderIndent(true, true);
31881         }
31882     },
31883
31884     // these methods are overridden to provide lazy rendering support
31885     // private override
31886     appendChild : function(){
31887         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31888         if(node && this.childrenRendered){
31889             node.render();
31890         }
31891         this.ui.updateExpandIcon();
31892         return node;
31893     },
31894
31895     // private override
31896     removeChild : function(node){
31897         this.ownerTree.getSelectionModel().unselect(node);
31898         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31899         // if it's been rendered remove dom node
31900         if(this.childrenRendered){
31901             node.ui.remove();
31902         }
31903         if(this.childNodes.length < 1){
31904             this.collapse(false, false);
31905         }else{
31906             this.ui.updateExpandIcon();
31907         }
31908         if(!this.firstChild) {
31909             this.childrenRendered = false;
31910         }
31911         return node;
31912     },
31913
31914     // private override
31915     insertBefore : function(node, refNode){
31916         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31917         if(newNode && refNode && this.childrenRendered){
31918             node.render();
31919         }
31920         this.ui.updateExpandIcon();
31921         return newNode;
31922     },
31923
31924     /**
31925      * Sets the text for this node
31926      * @param {String} text
31927      */
31928     setText : function(text){
31929         var oldText = this.text;
31930         this.text = text;
31931         this.attributes.text = text;
31932         if(this.rendered){ // event without subscribing
31933             this.ui.onTextChange(this, text, oldText);
31934         }
31935         this.fireEvent("textchange", this, text, oldText);
31936     },
31937
31938     /**
31939      * Triggers selection of this node
31940      */
31941     select : function(){
31942         this.getOwnerTree().getSelectionModel().select(this);
31943     },
31944
31945     /**
31946      * Triggers deselection of this node
31947      */
31948     unselect : function(){
31949         this.getOwnerTree().getSelectionModel().unselect(this);
31950     },
31951
31952     /**
31953      * Returns true if this node is selected
31954      * @return {Boolean}
31955      */
31956     isSelected : function(){
31957         return this.getOwnerTree().getSelectionModel().isSelected(this);
31958     },
31959
31960     /**
31961      * Expand this node.
31962      * @param {Boolean} deep (optional) True to expand all children as well
31963      * @param {Boolean} anim (optional) false to cancel the default animation
31964      * @param {Function} callback (optional) A callback to be called when
31965      * expanding this node completes (does not wait for deep expand to complete).
31966      * Called with 1 parameter, this node.
31967      */
31968     expand : function(deep, anim, callback){
31969         if(!this.expanded){
31970             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31971                 return;
31972             }
31973             if(!this.childrenRendered){
31974                 this.renderChildren();
31975             }
31976             this.expanded = true;
31977             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31978                 this.ui.animExpand(function(){
31979                     this.fireEvent("expand", this);
31980                     if(typeof callback == "function"){
31981                         callback(this);
31982                     }
31983                     if(deep === true){
31984                         this.expandChildNodes(true);
31985                     }
31986                 }.createDelegate(this));
31987                 return;
31988             }else{
31989                 this.ui.expand();
31990                 this.fireEvent("expand", this);
31991                 if(typeof callback == "function"){
31992                     callback(this);
31993                 }
31994             }
31995         }else{
31996            if(typeof callback == "function"){
31997                callback(this);
31998            }
31999         }
32000         if(deep === true){
32001             this.expandChildNodes(true);
32002         }
32003     },
32004
32005     isHiddenRoot : function(){
32006         return this.isRoot && !this.getOwnerTree().rootVisible;
32007     },
32008
32009     /**
32010      * Collapse this node.
32011      * @param {Boolean} deep (optional) True to collapse all children as well
32012      * @param {Boolean} anim (optional) false to cancel the default animation
32013      */
32014     collapse : function(deep, anim){
32015         if(this.expanded && !this.isHiddenRoot()){
32016             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
32017                 return;
32018             }
32019             this.expanded = false;
32020             if((this.getOwnerTree().animate && anim !== false) || anim){
32021                 this.ui.animCollapse(function(){
32022                     this.fireEvent("collapse", this);
32023                     if(deep === true){
32024                         this.collapseChildNodes(true);
32025                     }
32026                 }.createDelegate(this));
32027                 return;
32028             }else{
32029                 this.ui.collapse();
32030                 this.fireEvent("collapse", this);
32031             }
32032         }
32033         if(deep === true){
32034             var cs = this.childNodes;
32035             for(var i = 0, len = cs.length; i < len; i++) {
32036                 cs[i].collapse(true, false);
32037             }
32038         }
32039     },
32040
32041     // private
32042     delayedExpand : function(delay){
32043         if(!this.expandProcId){
32044             this.expandProcId = this.expand.defer(delay, this);
32045         }
32046     },
32047
32048     // private
32049     cancelExpand : function(){
32050         if(this.expandProcId){
32051             clearTimeout(this.expandProcId);
32052         }
32053         this.expandProcId = false;
32054     },
32055
32056     /**
32057      * Toggles expanded/collapsed state of the node
32058      */
32059     toggle : function(){
32060         if(this.expanded){
32061             this.collapse();
32062         }else{
32063             this.expand();
32064         }
32065     },
32066
32067     /**
32068      * Ensures all parent nodes are expanded
32069      */
32070     ensureVisible : function(callback){
32071         var tree = this.getOwnerTree();
32072         tree.expandPath(this.parentNode.getPath(), false, function(){
32073             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
32074             Roo.callback(callback);
32075         }.createDelegate(this));
32076     },
32077
32078     /**
32079      * Expand all child nodes
32080      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
32081      */
32082     expandChildNodes : function(deep){
32083         var cs = this.childNodes;
32084         for(var i = 0, len = cs.length; i < len; i++) {
32085                 cs[i].expand(deep);
32086         }
32087     },
32088
32089     /**
32090      * Collapse all child nodes
32091      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
32092      */
32093     collapseChildNodes : function(deep){
32094         var cs = this.childNodes;
32095         for(var i = 0, len = cs.length; i < len; i++) {
32096                 cs[i].collapse(deep);
32097         }
32098     },
32099
32100     /**
32101      * Disables this node
32102      */
32103     disable : function(){
32104         this.disabled = true;
32105         this.unselect();
32106         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32107             this.ui.onDisableChange(this, true);
32108         }
32109         this.fireEvent("disabledchange", this, true);
32110     },
32111
32112     /**
32113      * Enables this node
32114      */
32115     enable : function(){
32116         this.disabled = false;
32117         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
32118             this.ui.onDisableChange(this, false);
32119         }
32120         this.fireEvent("disabledchange", this, false);
32121     },
32122
32123     // private
32124     renderChildren : function(suppressEvent){
32125         if(suppressEvent !== false){
32126             this.fireEvent("beforechildrenrendered", this);
32127         }
32128         var cs = this.childNodes;
32129         for(var i = 0, len = cs.length; i < len; i++){
32130             cs[i].render(true);
32131         }
32132         this.childrenRendered = true;
32133     },
32134
32135     // private
32136     sort : function(fn, scope){
32137         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
32138         if(this.childrenRendered){
32139             var cs = this.childNodes;
32140             for(var i = 0, len = cs.length; i < len; i++){
32141                 cs[i].render(true);
32142             }
32143         }
32144     },
32145
32146     // private
32147     render : function(bulkRender){
32148         this.ui.render(bulkRender);
32149         if(!this.rendered){
32150             this.rendered = true;
32151             if(this.expanded){
32152                 this.expanded = false;
32153                 this.expand(false, false);
32154             }
32155         }
32156     },
32157
32158     // private
32159     renderIndent : function(deep, refresh){
32160         if(refresh){
32161             this.ui.childIndent = null;
32162         }
32163         this.ui.renderIndent();
32164         if(deep === true && this.childrenRendered){
32165             var cs = this.childNodes;
32166             for(var i = 0, len = cs.length; i < len; i++){
32167                 cs[i].renderIndent(true, refresh);
32168             }
32169         }
32170     }
32171 });/*
32172  * Based on:
32173  * Ext JS Library 1.1.1
32174  * Copyright(c) 2006-2007, Ext JS, LLC.
32175  *
32176  * Originally Released Under LGPL - original licence link has changed is not relivant.
32177  *
32178  * Fork - LGPL
32179  * <script type="text/javascript">
32180  */
32181  
32182 /**
32183  * @class Roo.tree.AsyncTreeNode
32184  * @extends Roo.tree.TreeNode
32185  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
32186  * @constructor
32187  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
32188  */
32189  Roo.tree.AsyncTreeNode = function(config){
32190     this.loaded = false;
32191     this.loading = false;
32192     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
32193     /**
32194     * @event beforeload
32195     * Fires before this node is loaded, return false to cancel
32196     * @param {Node} this This node
32197     */
32198     this.addEvents({'beforeload':true, 'load': true});
32199     /**
32200     * @event load
32201     * Fires when this node is loaded
32202     * @param {Node} this This node
32203     */
32204     /**
32205      * The loader used by this node (defaults to using the tree's defined loader)
32206      * @type TreeLoader
32207      * @property loader
32208      */
32209 };
32210 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
32211     expand : function(deep, anim, callback){
32212         if(this.loading){ // if an async load is already running, waiting til it's done
32213             var timer;
32214             var f = function(){
32215                 if(!this.loading){ // done loading
32216                     clearInterval(timer);
32217                     this.expand(deep, anim, callback);
32218                 }
32219             }.createDelegate(this);
32220             timer = setInterval(f, 200);
32221             return;
32222         }
32223         if(!this.loaded){
32224             if(this.fireEvent("beforeload", this) === false){
32225                 return;
32226             }
32227             this.loading = true;
32228             this.ui.beforeLoad(this);
32229             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
32230             if(loader){
32231                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
32232                 return;
32233             }
32234         }
32235         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
32236     },
32237     
32238     /**
32239      * Returns true if this node is currently loading
32240      * @return {Boolean}
32241      */
32242     isLoading : function(){
32243         return this.loading;  
32244     },
32245     
32246     loadComplete : function(deep, anim, callback){
32247         this.loading = false;
32248         this.loaded = true;
32249         this.ui.afterLoad(this);
32250         this.fireEvent("load", this);
32251         this.expand(deep, anim, callback);
32252     },
32253     
32254     /**
32255      * Returns true if this node has been loaded
32256      * @return {Boolean}
32257      */
32258     isLoaded : function(){
32259         return this.loaded;
32260     },
32261     
32262     hasChildNodes : function(){
32263         if(!this.isLeaf() && !this.loaded){
32264             return true;
32265         }else{
32266             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32267         }
32268     },
32269
32270     /**
32271      * Trigger a reload for this node
32272      * @param {Function} callback
32273      */
32274     reload : function(callback){
32275         this.collapse(false, false);
32276         while(this.firstChild){
32277             this.removeChild(this.firstChild);
32278         }
32279         this.childrenRendered = false;
32280         this.loaded = false;
32281         if(this.isHiddenRoot()){
32282             this.expanded = false;
32283         }
32284         this.expand(false, false, callback);
32285     }
32286 });/*
32287  * Based on:
32288  * Ext JS Library 1.1.1
32289  * Copyright(c) 2006-2007, Ext JS, LLC.
32290  *
32291  * Originally Released Under LGPL - original licence link has changed is not relivant.
32292  *
32293  * Fork - LGPL
32294  * <script type="text/javascript">
32295  */
32296  
32297 /**
32298  * @class Roo.tree.TreeNodeUI
32299  * @constructor
32300  * @param {Object} node The node to render
32301  * The TreeNode UI implementation is separate from the
32302  * tree implementation. Unless you are customizing the tree UI,
32303  * you should never have to use this directly.
32304  */
32305 Roo.tree.TreeNodeUI = function(node){
32306     this.node = node;
32307     this.rendered = false;
32308     this.animating = false;
32309     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32310 };
32311
32312 Roo.tree.TreeNodeUI.prototype = {
32313     removeChild : function(node){
32314         if(this.rendered){
32315             this.ctNode.removeChild(node.ui.getEl());
32316         }
32317     },
32318
32319     beforeLoad : function(){
32320          this.addClass("x-tree-node-loading");
32321     },
32322
32323     afterLoad : function(){
32324          this.removeClass("x-tree-node-loading");
32325     },
32326
32327     onTextChange : function(node, text, oldText){
32328         if(this.rendered){
32329             this.textNode.innerHTML = text;
32330         }
32331     },
32332
32333     onDisableChange : function(node, state){
32334         this.disabled = state;
32335         if(state){
32336             this.addClass("x-tree-node-disabled");
32337         }else{
32338             this.removeClass("x-tree-node-disabled");
32339         }
32340     },
32341
32342     onSelectedChange : function(state){
32343         if(state){
32344             this.focus();
32345             this.addClass("x-tree-selected");
32346         }else{
32347             //this.blur();
32348             this.removeClass("x-tree-selected");
32349         }
32350     },
32351
32352     onMove : function(tree, node, oldParent, newParent, index, refNode){
32353         this.childIndent = null;
32354         if(this.rendered){
32355             var targetNode = newParent.ui.getContainer();
32356             if(!targetNode){//target not rendered
32357                 this.holder = document.createElement("div");
32358                 this.holder.appendChild(this.wrap);
32359                 return;
32360             }
32361             var insertBefore = refNode ? refNode.ui.getEl() : null;
32362             if(insertBefore){
32363                 targetNode.insertBefore(this.wrap, insertBefore);
32364             }else{
32365                 targetNode.appendChild(this.wrap);
32366             }
32367             this.node.renderIndent(true);
32368         }
32369     },
32370
32371     addClass : function(cls){
32372         if(this.elNode){
32373             Roo.fly(this.elNode).addClass(cls);
32374         }
32375     },
32376
32377     removeClass : function(cls){
32378         if(this.elNode){
32379             Roo.fly(this.elNode).removeClass(cls);
32380         }
32381     },
32382
32383     remove : function(){
32384         if(this.rendered){
32385             this.holder = document.createElement("div");
32386             this.holder.appendChild(this.wrap);
32387         }
32388     },
32389
32390     fireEvent : function(){
32391         return this.node.fireEvent.apply(this.node, arguments);
32392     },
32393
32394     initEvents : function(){
32395         this.node.on("move", this.onMove, this);
32396         var E = Roo.EventManager;
32397         var a = this.anchor;
32398
32399         var el = Roo.fly(a, '_treeui');
32400
32401         if(Roo.isOpera){ // opera render bug ignores the CSS
32402             el.setStyle("text-decoration", "none");
32403         }
32404
32405         el.on("click", this.onClick, this);
32406         el.on("dblclick", this.onDblClick, this);
32407
32408         if(this.checkbox){
32409             Roo.EventManager.on(this.checkbox,
32410                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32411         }
32412
32413         el.on("contextmenu", this.onContextMenu, this);
32414
32415         var icon = Roo.fly(this.iconNode);
32416         icon.on("click", this.onClick, this);
32417         icon.on("dblclick", this.onDblClick, this);
32418         icon.on("contextmenu", this.onContextMenu, this);
32419         E.on(this.ecNode, "click", this.ecClick, this, true);
32420
32421         if(this.node.disabled){
32422             this.addClass("x-tree-node-disabled");
32423         }
32424         if(this.node.hidden){
32425             this.addClass("x-tree-node-disabled");
32426         }
32427         var ot = this.node.getOwnerTree();
32428         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32429         if(dd && (!this.node.isRoot || ot.rootVisible)){
32430             Roo.dd.Registry.register(this.elNode, {
32431                 node: this.node,
32432                 handles: this.getDDHandles(),
32433                 isHandle: false
32434             });
32435         }
32436     },
32437
32438     getDDHandles : function(){
32439         return [this.iconNode, this.textNode];
32440     },
32441
32442     hide : function(){
32443         if(this.rendered){
32444             this.wrap.style.display = "none";
32445         }
32446     },
32447
32448     show : function(){
32449         if(this.rendered){
32450             this.wrap.style.display = "";
32451         }
32452     },
32453
32454     onContextMenu : function(e){
32455         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32456             e.preventDefault();
32457             this.focus();
32458             this.fireEvent("contextmenu", this.node, e);
32459         }
32460     },
32461
32462     onClick : function(e){
32463         if(this.dropping){
32464             e.stopEvent();
32465             return;
32466         }
32467         if(this.fireEvent("beforeclick", this.node, e) !== false){
32468             if(!this.disabled && this.node.attributes.href){
32469                 this.fireEvent("click", this.node, e);
32470                 return;
32471             }
32472             e.preventDefault();
32473             if(this.disabled){
32474                 return;
32475             }
32476
32477             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32478                 this.node.toggle();
32479             }
32480
32481             this.fireEvent("click", this.node, e);
32482         }else{
32483             e.stopEvent();
32484         }
32485     },
32486
32487     onDblClick : function(e){
32488         e.preventDefault();
32489         if(this.disabled){
32490             return;
32491         }
32492         if(this.checkbox){
32493             this.toggleCheck();
32494         }
32495         if(!this.animating && this.node.hasChildNodes()){
32496             this.node.toggle();
32497         }
32498         this.fireEvent("dblclick", this.node, e);
32499     },
32500
32501     onCheckChange : function(){
32502         var checked = this.checkbox.checked;
32503         this.node.attributes.checked = checked;
32504         this.fireEvent('checkchange', this.node, checked);
32505     },
32506
32507     ecClick : function(e){
32508         if(!this.animating && this.node.hasChildNodes()){
32509             this.node.toggle();
32510         }
32511     },
32512
32513     startDrop : function(){
32514         this.dropping = true;
32515     },
32516
32517     // delayed drop so the click event doesn't get fired on a drop
32518     endDrop : function(){
32519        setTimeout(function(){
32520            this.dropping = false;
32521        }.createDelegate(this), 50);
32522     },
32523
32524     expand : function(){
32525         this.updateExpandIcon();
32526         this.ctNode.style.display = "";
32527     },
32528
32529     focus : function(){
32530         if(!this.node.preventHScroll){
32531             try{this.anchor.focus();
32532             }catch(e){}
32533         }else if(!Roo.isIE){
32534             try{
32535                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32536                 var l = noscroll.scrollLeft;
32537                 this.anchor.focus();
32538                 noscroll.scrollLeft = l;
32539             }catch(e){}
32540         }
32541     },
32542
32543     toggleCheck : function(value){
32544         var cb = this.checkbox;
32545         if(cb){
32546             cb.checked = (value === undefined ? !cb.checked : value);
32547         }
32548     },
32549
32550     blur : function(){
32551         try{
32552             this.anchor.blur();
32553         }catch(e){}
32554     },
32555
32556     animExpand : function(callback){
32557         var ct = Roo.get(this.ctNode);
32558         ct.stopFx();
32559         if(!this.node.hasChildNodes()){
32560             this.updateExpandIcon();
32561             this.ctNode.style.display = "";
32562             Roo.callback(callback);
32563             return;
32564         }
32565         this.animating = true;
32566         this.updateExpandIcon();
32567
32568         ct.slideIn('t', {
32569            callback : function(){
32570                this.animating = false;
32571                Roo.callback(callback);
32572             },
32573             scope: this,
32574             duration: this.node.ownerTree.duration || .25
32575         });
32576     },
32577
32578     highlight : function(){
32579         var tree = this.node.getOwnerTree();
32580         Roo.fly(this.wrap).highlight(
32581             tree.hlColor || "C3DAF9",
32582             {endColor: tree.hlBaseColor}
32583         );
32584     },
32585
32586     collapse : function(){
32587         this.updateExpandIcon();
32588         this.ctNode.style.display = "none";
32589     },
32590
32591     animCollapse : function(callback){
32592         var ct = Roo.get(this.ctNode);
32593         ct.enableDisplayMode('block');
32594         ct.stopFx();
32595
32596         this.animating = true;
32597         this.updateExpandIcon();
32598
32599         ct.slideOut('t', {
32600             callback : function(){
32601                this.animating = false;
32602                Roo.callback(callback);
32603             },
32604             scope: this,
32605             duration: this.node.ownerTree.duration || .25
32606         });
32607     },
32608
32609     getContainer : function(){
32610         return this.ctNode;
32611     },
32612
32613     getEl : function(){
32614         return this.wrap;
32615     },
32616
32617     appendDDGhost : function(ghostNode){
32618         ghostNode.appendChild(this.elNode.cloneNode(true));
32619     },
32620
32621     getDDRepairXY : function(){
32622         return Roo.lib.Dom.getXY(this.iconNode);
32623     },
32624
32625     onRender : function(){
32626         this.render();
32627     },
32628
32629     render : function(bulkRender){
32630         var n = this.node, a = n.attributes;
32631         var targetNode = n.parentNode ?
32632               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32633
32634         if(!this.rendered){
32635             this.rendered = true;
32636
32637             this.renderElements(n, a, targetNode, bulkRender);
32638
32639             if(a.qtip){
32640                if(this.textNode.setAttributeNS){
32641                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32642                    if(a.qtipTitle){
32643                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32644                    }
32645                }else{
32646                    this.textNode.setAttribute("ext:qtip", a.qtip);
32647                    if(a.qtipTitle){
32648                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32649                    }
32650                }
32651             }else if(a.qtipCfg){
32652                 a.qtipCfg.target = Roo.id(this.textNode);
32653                 Roo.QuickTips.register(a.qtipCfg);
32654             }
32655             this.initEvents();
32656             if(!this.node.expanded){
32657                 this.updateExpandIcon();
32658             }
32659         }else{
32660             if(bulkRender === true) {
32661                 targetNode.appendChild(this.wrap);
32662             }
32663         }
32664     },
32665
32666     renderElements : function(n, a, targetNode, bulkRender)
32667     {
32668         // add some indent caching, this helps performance when rendering a large tree
32669         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32670         var t = n.getOwnerTree();
32671         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32672         if (typeof(n.attributes.html) != 'undefined') {
32673             txt = n.attributes.html;
32674         }
32675         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32676         var cb = typeof a.checked == 'boolean';
32677         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32678         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32679             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32680             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32681             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32682             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32683             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32684              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32685                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32686             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32687             "</li>"];
32688
32689         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32690             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32691                                 n.nextSibling.ui.getEl(), buf.join(""));
32692         }else{
32693             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32694         }
32695
32696         this.elNode = this.wrap.childNodes[0];
32697         this.ctNode = this.wrap.childNodes[1];
32698         var cs = this.elNode.childNodes;
32699         this.indentNode = cs[0];
32700         this.ecNode = cs[1];
32701         this.iconNode = cs[2];
32702         var index = 3;
32703         if(cb){
32704             this.checkbox = cs[3];
32705             index++;
32706         }
32707         this.anchor = cs[index];
32708         this.textNode = cs[index].firstChild;
32709     },
32710
32711     getAnchor : function(){
32712         return this.anchor;
32713     },
32714
32715     getTextEl : function(){
32716         return this.textNode;
32717     },
32718
32719     getIconEl : function(){
32720         return this.iconNode;
32721     },
32722
32723     isChecked : function(){
32724         return this.checkbox ? this.checkbox.checked : false;
32725     },
32726
32727     updateExpandIcon : function(){
32728         if(this.rendered){
32729             var n = this.node, c1, c2;
32730             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32731             var hasChild = n.hasChildNodes();
32732             if(hasChild){
32733                 if(n.expanded){
32734                     cls += "-minus";
32735                     c1 = "x-tree-node-collapsed";
32736                     c2 = "x-tree-node-expanded";
32737                 }else{
32738                     cls += "-plus";
32739                     c1 = "x-tree-node-expanded";
32740                     c2 = "x-tree-node-collapsed";
32741                 }
32742                 if(this.wasLeaf){
32743                     this.removeClass("x-tree-node-leaf");
32744                     this.wasLeaf = false;
32745                 }
32746                 if(this.c1 != c1 || this.c2 != c2){
32747                     Roo.fly(this.elNode).replaceClass(c1, c2);
32748                     this.c1 = c1; this.c2 = c2;
32749                 }
32750             }else{
32751                 // this changes non-leafs into leafs if they have no children.
32752                 // it's not very rational behaviour..
32753                 
32754                 if(!this.wasLeaf && this.node.leaf){
32755                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32756                     delete this.c1;
32757                     delete this.c2;
32758                     this.wasLeaf = true;
32759                 }
32760             }
32761             var ecc = "x-tree-ec-icon "+cls;
32762             if(this.ecc != ecc){
32763                 this.ecNode.className = ecc;
32764                 this.ecc = ecc;
32765             }
32766         }
32767     },
32768
32769     getChildIndent : function(){
32770         if(!this.childIndent){
32771             var buf = [];
32772             var p = this.node;
32773             while(p){
32774                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32775                     if(!p.isLast()) {
32776                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32777                     } else {
32778                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32779                     }
32780                 }
32781                 p = p.parentNode;
32782             }
32783             this.childIndent = buf.join("");
32784         }
32785         return this.childIndent;
32786     },
32787
32788     renderIndent : function(){
32789         if(this.rendered){
32790             var indent = "";
32791             var p = this.node.parentNode;
32792             if(p){
32793                 indent = p.ui.getChildIndent();
32794             }
32795             if(this.indentMarkup != indent){ // don't rerender if not required
32796                 this.indentNode.innerHTML = indent;
32797                 this.indentMarkup = indent;
32798             }
32799             this.updateExpandIcon();
32800         }
32801     }
32802 };
32803
32804 Roo.tree.RootTreeNodeUI = function(){
32805     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32806 };
32807 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32808     render : function(){
32809         if(!this.rendered){
32810             var targetNode = this.node.ownerTree.innerCt.dom;
32811             this.node.expanded = true;
32812             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32813             this.wrap = this.ctNode = targetNode.firstChild;
32814         }
32815     },
32816     collapse : function(){
32817     },
32818     expand : function(){
32819     }
32820 });/*
32821  * Based on:
32822  * Ext JS Library 1.1.1
32823  * Copyright(c) 2006-2007, Ext JS, LLC.
32824  *
32825  * Originally Released Under LGPL - original licence link has changed is not relivant.
32826  *
32827  * Fork - LGPL
32828  * <script type="text/javascript">
32829  */
32830 /**
32831  * @class Roo.tree.TreeLoader
32832  * @extends Roo.util.Observable
32833  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32834  * nodes from a specified URL. The response must be a javascript Array definition
32835  * who's elements are node definition objects. eg:
32836  * <pre><code>
32837    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32838     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32839 </code></pre>
32840  * <br><br>
32841  * A server request is sent, and child nodes are loaded only when a node is expanded.
32842  * The loading node's id is passed to the server under the parameter name "node" to
32843  * enable the server to produce the correct child nodes.
32844  * <br><br>
32845  * To pass extra parameters, an event handler may be attached to the "beforeload"
32846  * event, and the parameters specified in the TreeLoader's baseParams property:
32847  * <pre><code>
32848     myTreeLoader.on("beforeload", function(treeLoader, node) {
32849         this.baseParams.category = node.attributes.category;
32850     }, this);
32851 </code></pre><
32852  * This would pass an HTTP parameter called "category" to the server containing
32853  * the value of the Node's "category" attribute.
32854  * @constructor
32855  * Creates a new Treeloader.
32856  * @param {Object} config A config object containing config properties.
32857  */
32858 Roo.tree.TreeLoader = function(config){
32859     this.baseParams = {};
32860     this.requestMethod = "POST";
32861     Roo.apply(this, config);
32862
32863     this.addEvents({
32864     
32865         /**
32866          * @event beforeload
32867          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32868          * @param {Object} This TreeLoader object.
32869          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32870          * @param {Object} callback The callback function specified in the {@link #load} call.
32871          */
32872         beforeload : true,
32873         /**
32874          * @event load
32875          * Fires when the node has been successfuly loaded.
32876          * @param {Object} This TreeLoader object.
32877          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32878          * @param {Object} response The response object containing the data from the server.
32879          */
32880         load : true,
32881         /**
32882          * @event loadexception
32883          * Fires if the network request failed.
32884          * @param {Object} This TreeLoader object.
32885          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32886          * @param {Object} response The response object containing the data from the server.
32887          */
32888         loadexception : true,
32889         /**
32890          * @event create
32891          * Fires before a node is created, enabling you to return custom Node types 
32892          * @param {Object} This TreeLoader object.
32893          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32894          */
32895         create : true
32896     });
32897
32898     Roo.tree.TreeLoader.superclass.constructor.call(this);
32899 };
32900
32901 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32902     /**
32903     * @cfg {String} dataUrl The URL from which to request a Json string which
32904     * specifies an array of node definition object representing the child nodes
32905     * to be loaded.
32906     */
32907     /**
32908     * @cfg {Object} baseParams (optional) An object containing properties which
32909     * specify HTTP parameters to be passed to each request for child nodes.
32910     */
32911     /**
32912     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32913     * created by this loader. If the attributes sent by the server have an attribute in this object,
32914     * they take priority.
32915     */
32916     /**
32917     * @cfg {Object} uiProviders (optional) An object containing properties which
32918     * 
32919     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
32920     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32921     * <i>uiProvider</i> attribute of a returned child node is a string rather
32922     * than a reference to a TreeNodeUI implementation, this that string value
32923     * is used as a property name in the uiProviders object. You can define the provider named
32924     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32925     */
32926     uiProviders : {},
32927
32928     /**
32929     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32930     * child nodes before loading.
32931     */
32932     clearOnLoad : true,
32933
32934     /**
32935     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32936     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32937     * Grid query { data : [ .....] }
32938     */
32939     
32940     root : false,
32941      /**
32942     * @cfg {String} queryParam (optional) 
32943     * Name of the query as it will be passed on the querystring (defaults to 'node')
32944     * eg. the request will be ?node=[id]
32945     */
32946     
32947     
32948     queryParam: false,
32949     
32950     /**
32951      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32952      * This is called automatically when a node is expanded, but may be used to reload
32953      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32954      * @param {Roo.tree.TreeNode} node
32955      * @param {Function} callback
32956      */
32957     load : function(node, callback){
32958         if(this.clearOnLoad){
32959             while(node.firstChild){
32960                 node.removeChild(node.firstChild);
32961             }
32962         }
32963         if(node.attributes.children){ // preloaded json children
32964             var cs = node.attributes.children;
32965             for(var i = 0, len = cs.length; i < len; i++){
32966                 node.appendChild(this.createNode(cs[i]));
32967             }
32968             if(typeof callback == "function"){
32969                 callback();
32970             }
32971         }else if(this.dataUrl){
32972             this.requestData(node, callback);
32973         }
32974     },
32975
32976     getParams: function(node){
32977         var buf = [], bp = this.baseParams;
32978         for(var key in bp){
32979             if(typeof bp[key] != "function"){
32980                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32981             }
32982         }
32983         var n = this.queryParam === false ? 'node' : this.queryParam;
32984         buf.push(n + "=", encodeURIComponent(node.id));
32985         return buf.join("");
32986     },
32987
32988     requestData : function(node, callback){
32989         if(this.fireEvent("beforeload", this, node, callback) !== false){
32990             this.transId = Roo.Ajax.request({
32991                 method:this.requestMethod,
32992                 url: this.dataUrl||this.url,
32993                 success: this.handleResponse,
32994                 failure: this.handleFailure,
32995                 scope: this,
32996                 argument: {callback: callback, node: node},
32997                 params: this.getParams(node)
32998             });
32999         }else{
33000             // if the load is cancelled, make sure we notify
33001             // the node that we are done
33002             if(typeof callback == "function"){
33003                 callback();
33004             }
33005         }
33006     },
33007
33008     isLoading : function(){
33009         return this.transId ? true : false;
33010     },
33011
33012     abort : function(){
33013         if(this.isLoading()){
33014             Roo.Ajax.abort(this.transId);
33015         }
33016     },
33017
33018     // private
33019     createNode : function(attr)
33020     {
33021         // apply baseAttrs, nice idea Corey!
33022         if(this.baseAttrs){
33023             Roo.applyIf(attr, this.baseAttrs);
33024         }
33025         if(this.applyLoader !== false){
33026             attr.loader = this;
33027         }
33028         // uiProvider = depreciated..
33029         
33030         if(typeof(attr.uiProvider) == 'string'){
33031            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
33032                 /**  eval:var:attr */ eval(attr.uiProvider);
33033         }
33034         if(typeof(this.uiProviders['default']) != 'undefined') {
33035             attr.uiProvider = this.uiProviders['default'];
33036         }
33037         
33038         this.fireEvent('create', this, attr);
33039         
33040         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
33041         return(attr.leaf ?
33042                         new Roo.tree.TreeNode(attr) :
33043                         new Roo.tree.AsyncTreeNode(attr));
33044     },
33045
33046     processResponse : function(response, node, callback)
33047     {
33048         var json = response.responseText;
33049         try {
33050             
33051             var o = Roo.decode(json);
33052             
33053             if (!o.success) {
33054                 // it's a failure condition.
33055                 var a = response.argument;
33056                 this.fireEvent("loadexception", this, a.node, response);
33057                 Roo.log("Load failed - should have a handler really");
33058                 return;
33059             }
33060             
33061             if (this.root !== false) {
33062                 o = o[this.root];
33063             }
33064             
33065             for(var i = 0, len = o.length; i < len; i++){
33066                 var n = this.createNode(o[i]);
33067                 if(n){
33068                     node.appendChild(n);
33069                 }
33070             }
33071             if(typeof callback == "function"){
33072                 callback(this, node);
33073             }
33074         }catch(e){
33075             this.handleFailure(response);
33076         }
33077     },
33078
33079     handleResponse : function(response){
33080         this.transId = false;
33081         var a = response.argument;
33082         this.processResponse(response, a.node, a.callback);
33083         this.fireEvent("load", this, a.node, response);
33084     },
33085
33086     handleFailure : function(response)
33087     {
33088         // should handle failure better..
33089         this.transId = false;
33090         var a = response.argument;
33091         this.fireEvent("loadexception", this, a.node, response);
33092         if(typeof a.callback == "function"){
33093             a.callback(this, a.node);
33094         }
33095     }
33096 });/*
33097  * Based on:
33098  * Ext JS Library 1.1.1
33099  * Copyright(c) 2006-2007, Ext JS, LLC.
33100  *
33101  * Originally Released Under LGPL - original licence link has changed is not relivant.
33102  *
33103  * Fork - LGPL
33104  * <script type="text/javascript">
33105  */
33106
33107 /**
33108 * @class Roo.tree.TreeFilter
33109 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
33110 * @param {TreePanel} tree
33111 * @param {Object} config (optional)
33112  */
33113 Roo.tree.TreeFilter = function(tree, config){
33114     this.tree = tree;
33115     this.filtered = {};
33116     Roo.apply(this, config);
33117 };
33118
33119 Roo.tree.TreeFilter.prototype = {
33120     clearBlank:false,
33121     reverse:false,
33122     autoClear:false,
33123     remove:false,
33124
33125      /**
33126      * Filter the data by a specific attribute.
33127      * @param {String/RegExp} value Either string that the attribute value
33128      * should start with or a RegExp to test against the attribute
33129      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
33130      * @param {TreeNode} startNode (optional) The node to start the filter at.
33131      */
33132     filter : function(value, attr, startNode){
33133         attr = attr || "text";
33134         var f;
33135         if(typeof value == "string"){
33136             var vlen = value.length;
33137             // auto clear empty filter
33138             if(vlen == 0 && this.clearBlank){
33139                 this.clear();
33140                 return;
33141             }
33142             value = value.toLowerCase();
33143             f = function(n){
33144                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
33145             };
33146         }else if(value.exec){ // regex?
33147             f = function(n){
33148                 return value.test(n.attributes[attr]);
33149             };
33150         }else{
33151             throw 'Illegal filter type, must be string or regex';
33152         }
33153         this.filterBy(f, null, startNode);
33154         },
33155
33156     /**
33157      * Filter by a function. The passed function will be called with each
33158      * node in the tree (or from the startNode). If the function returns true, the node is kept
33159      * otherwise it is filtered. If a node is filtered, its children are also filtered.
33160      * @param {Function} fn The filter function
33161      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
33162      */
33163     filterBy : function(fn, scope, startNode){
33164         startNode = startNode || this.tree.root;
33165         if(this.autoClear){
33166             this.clear();
33167         }
33168         var af = this.filtered, rv = this.reverse;
33169         var f = function(n){
33170             if(n == startNode){
33171                 return true;
33172             }
33173             if(af[n.id]){
33174                 return false;
33175             }
33176             var m = fn.call(scope || n, n);
33177             if(!m || rv){
33178                 af[n.id] = n;
33179                 n.ui.hide();
33180                 return false;
33181             }
33182             return true;
33183         };
33184         startNode.cascade(f);
33185         if(this.remove){
33186            for(var id in af){
33187                if(typeof id != "function"){
33188                    var n = af[id];
33189                    if(n && n.parentNode){
33190                        n.parentNode.removeChild(n);
33191                    }
33192                }
33193            }
33194         }
33195     },
33196
33197     /**
33198      * Clears the current filter. Note: with the "remove" option
33199      * set a filter cannot be cleared.
33200      */
33201     clear : function(){
33202         var t = this.tree;
33203         var af = this.filtered;
33204         for(var id in af){
33205             if(typeof id != "function"){
33206                 var n = af[id];
33207                 if(n){
33208                     n.ui.show();
33209                 }
33210             }
33211         }
33212         this.filtered = {};
33213     }
33214 };
33215 /*
33216  * Based on:
33217  * Ext JS Library 1.1.1
33218  * Copyright(c) 2006-2007, Ext JS, LLC.
33219  *
33220  * Originally Released Under LGPL - original licence link has changed is not relivant.
33221  *
33222  * Fork - LGPL
33223  * <script type="text/javascript">
33224  */
33225  
33226
33227 /**
33228  * @class Roo.tree.TreeSorter
33229  * Provides sorting of nodes in a TreePanel
33230  * 
33231  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
33232  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
33233  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
33234  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
33235  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
33236  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
33237  * @constructor
33238  * @param {TreePanel} tree
33239  * @param {Object} config
33240  */
33241 Roo.tree.TreeSorter = function(tree, config){
33242     Roo.apply(this, config);
33243     tree.on("beforechildrenrendered", this.doSort, this);
33244     tree.on("append", this.updateSort, this);
33245     tree.on("insert", this.updateSort, this);
33246     
33247     var dsc = this.dir && this.dir.toLowerCase() == "desc";
33248     var p = this.property || "text";
33249     var sortType = this.sortType;
33250     var fs = this.folderSort;
33251     var cs = this.caseSensitive === true;
33252     var leafAttr = this.leafAttr || 'leaf';
33253
33254     this.sortFn = function(n1, n2){
33255         if(fs){
33256             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
33257                 return 1;
33258             }
33259             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
33260                 return -1;
33261             }
33262         }
33263         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
33264         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
33265         if(v1 < v2){
33266                         return dsc ? +1 : -1;
33267                 }else if(v1 > v2){
33268                         return dsc ? -1 : +1;
33269         }else{
33270                 return 0;
33271         }
33272     };
33273 };
33274
33275 Roo.tree.TreeSorter.prototype = {
33276     doSort : function(node){
33277         node.sort(this.sortFn);
33278     },
33279     
33280     compareNodes : function(n1, n2){
33281         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33282     },
33283     
33284     updateSort : function(tree, node){
33285         if(node.childrenRendered){
33286             this.doSort.defer(1, this, [node]);
33287         }
33288     }
33289 };/*
33290  * Based on:
33291  * Ext JS Library 1.1.1
33292  * Copyright(c) 2006-2007, Ext JS, LLC.
33293  *
33294  * Originally Released Under LGPL - original licence link has changed is not relivant.
33295  *
33296  * Fork - LGPL
33297  * <script type="text/javascript">
33298  */
33299
33300 if(Roo.dd.DropZone){
33301     
33302 Roo.tree.TreeDropZone = function(tree, config){
33303     this.allowParentInsert = false;
33304     this.allowContainerDrop = false;
33305     this.appendOnly = false;
33306     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33307     this.tree = tree;
33308     this.lastInsertClass = "x-tree-no-status";
33309     this.dragOverData = {};
33310 };
33311
33312 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33313     ddGroup : "TreeDD",
33314     
33315     expandDelay : 1000,
33316     
33317     expandNode : function(node){
33318         if(node.hasChildNodes() && !node.isExpanded()){
33319             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33320         }
33321     },
33322     
33323     queueExpand : function(node){
33324         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33325     },
33326     
33327     cancelExpand : function(){
33328         if(this.expandProcId){
33329             clearTimeout(this.expandProcId);
33330             this.expandProcId = false;
33331         }
33332     },
33333     
33334     isValidDropPoint : function(n, pt, dd, e, data){
33335         if(!n || !data){ return false; }
33336         var targetNode = n.node;
33337         var dropNode = data.node;
33338         // default drop rules
33339         if(!(targetNode && targetNode.isTarget && pt)){
33340             return false;
33341         }
33342         if(pt == "append" && targetNode.allowChildren === false){
33343             return false;
33344         }
33345         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33346             return false;
33347         }
33348         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33349             return false;
33350         }
33351         // reuse the object
33352         var overEvent = this.dragOverData;
33353         overEvent.tree = this.tree;
33354         overEvent.target = targetNode;
33355         overEvent.data = data;
33356         overEvent.point = pt;
33357         overEvent.source = dd;
33358         overEvent.rawEvent = e;
33359         overEvent.dropNode = dropNode;
33360         overEvent.cancel = false;  
33361         var result = this.tree.fireEvent("nodedragover", overEvent);
33362         return overEvent.cancel === false && result !== false;
33363     },
33364     
33365     getDropPoint : function(e, n, dd){
33366         var tn = n.node;
33367         if(tn.isRoot){
33368             return tn.allowChildren !== false ? "append" : false; // always append for root
33369         }
33370         var dragEl = n.ddel;
33371         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33372         var y = Roo.lib.Event.getPageY(e);
33373         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33374         
33375         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33376         var noAppend = tn.allowChildren === false;
33377         if(this.appendOnly || tn.parentNode.allowChildren === false){
33378             return noAppend ? false : "append";
33379         }
33380         var noBelow = false;
33381         if(!this.allowParentInsert){
33382             noBelow = tn.hasChildNodes() && tn.isExpanded();
33383         }
33384         var q = (b - t) / (noAppend ? 2 : 3);
33385         if(y >= t && y < (t + q)){
33386             return "above";
33387         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33388             return "below";
33389         }else{
33390             return "append";
33391         }
33392     },
33393     
33394     onNodeEnter : function(n, dd, e, data){
33395         this.cancelExpand();
33396     },
33397     
33398     onNodeOver : function(n, dd, e, data){
33399         var pt = this.getDropPoint(e, n, dd);
33400         var node = n.node;
33401         
33402         // auto node expand check
33403         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33404             this.queueExpand(node);
33405         }else if(pt != "append"){
33406             this.cancelExpand();
33407         }
33408         
33409         // set the insert point style on the target node
33410         var returnCls = this.dropNotAllowed;
33411         if(this.isValidDropPoint(n, pt, dd, e, data)){
33412            if(pt){
33413                var el = n.ddel;
33414                var cls;
33415                if(pt == "above"){
33416                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33417                    cls = "x-tree-drag-insert-above";
33418                }else if(pt == "below"){
33419                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33420                    cls = "x-tree-drag-insert-below";
33421                }else{
33422                    returnCls = "x-tree-drop-ok-append";
33423                    cls = "x-tree-drag-append";
33424                }
33425                if(this.lastInsertClass != cls){
33426                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33427                    this.lastInsertClass = cls;
33428                }
33429            }
33430        }
33431        return returnCls;
33432     },
33433     
33434     onNodeOut : function(n, dd, e, data){
33435         this.cancelExpand();
33436         this.removeDropIndicators(n);
33437     },
33438     
33439     onNodeDrop : function(n, dd, e, data){
33440         var point = this.getDropPoint(e, n, dd);
33441         var targetNode = n.node;
33442         targetNode.ui.startDrop();
33443         if(!this.isValidDropPoint(n, point, dd, e, data)){
33444             targetNode.ui.endDrop();
33445             return false;
33446         }
33447         // first try to find the drop node
33448         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33449         var dropEvent = {
33450             tree : this.tree,
33451             target: targetNode,
33452             data: data,
33453             point: point,
33454             source: dd,
33455             rawEvent: e,
33456             dropNode: dropNode,
33457             cancel: !dropNode   
33458         };
33459         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33460         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33461             targetNode.ui.endDrop();
33462             return false;
33463         }
33464         // allow target changing
33465         targetNode = dropEvent.target;
33466         if(point == "append" && !targetNode.isExpanded()){
33467             targetNode.expand(false, null, function(){
33468                 this.completeDrop(dropEvent);
33469             }.createDelegate(this));
33470         }else{
33471             this.completeDrop(dropEvent);
33472         }
33473         return true;
33474     },
33475     
33476     completeDrop : function(de){
33477         var ns = de.dropNode, p = de.point, t = de.target;
33478         if(!(ns instanceof Array)){
33479             ns = [ns];
33480         }
33481         var n;
33482         for(var i = 0, len = ns.length; i < len; i++){
33483             n = ns[i];
33484             if(p == "above"){
33485                 t.parentNode.insertBefore(n, t);
33486             }else if(p == "below"){
33487                 t.parentNode.insertBefore(n, t.nextSibling);
33488             }else{
33489                 t.appendChild(n);
33490             }
33491         }
33492         n.ui.focus();
33493         if(this.tree.hlDrop){
33494             n.ui.highlight();
33495         }
33496         t.ui.endDrop();
33497         this.tree.fireEvent("nodedrop", de);
33498     },
33499     
33500     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33501         if(this.tree.hlDrop){
33502             dropNode.ui.focus();
33503             dropNode.ui.highlight();
33504         }
33505         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33506     },
33507     
33508     getTree : function(){
33509         return this.tree;
33510     },
33511     
33512     removeDropIndicators : function(n){
33513         if(n && n.ddel){
33514             var el = n.ddel;
33515             Roo.fly(el).removeClass([
33516                     "x-tree-drag-insert-above",
33517                     "x-tree-drag-insert-below",
33518                     "x-tree-drag-append"]);
33519             this.lastInsertClass = "_noclass";
33520         }
33521     },
33522     
33523     beforeDragDrop : function(target, e, id){
33524         this.cancelExpand();
33525         return true;
33526     },
33527     
33528     afterRepair : function(data){
33529         if(data && Roo.enableFx){
33530             data.node.ui.highlight();
33531         }
33532         this.hideProxy();
33533     }    
33534 });
33535
33536 }
33537 /*
33538  * Based on:
33539  * Ext JS Library 1.1.1
33540  * Copyright(c) 2006-2007, Ext JS, LLC.
33541  *
33542  * Originally Released Under LGPL - original licence link has changed is not relivant.
33543  *
33544  * Fork - LGPL
33545  * <script type="text/javascript">
33546  */
33547  
33548
33549 if(Roo.dd.DragZone){
33550 Roo.tree.TreeDragZone = function(tree, config){
33551     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33552     this.tree = tree;
33553 };
33554
33555 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33556     ddGroup : "TreeDD",
33557     
33558     onBeforeDrag : function(data, e){
33559         var n = data.node;
33560         return n && n.draggable && !n.disabled;
33561     },
33562     
33563     onInitDrag : function(e){
33564         var data = this.dragData;
33565         this.tree.getSelectionModel().select(data.node);
33566         this.proxy.update("");
33567         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33568         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33569     },
33570     
33571     getRepairXY : function(e, data){
33572         return data.node.ui.getDDRepairXY();
33573     },
33574     
33575     onEndDrag : function(data, e){
33576         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33577     },
33578     
33579     onValidDrop : function(dd, e, id){
33580         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33581         this.hideProxy();
33582     },
33583     
33584     beforeInvalidDrop : function(e, id){
33585         // this scrolls the original position back into view
33586         var sm = this.tree.getSelectionModel();
33587         sm.clearSelections();
33588         sm.select(this.dragData.node);
33589     }
33590 });
33591 }/*
33592  * Based on:
33593  * Ext JS Library 1.1.1
33594  * Copyright(c) 2006-2007, Ext JS, LLC.
33595  *
33596  * Originally Released Under LGPL - original licence link has changed is not relivant.
33597  *
33598  * Fork - LGPL
33599  * <script type="text/javascript">
33600  */
33601 /**
33602  * @class Roo.tree.TreeEditor
33603  * @extends Roo.Editor
33604  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33605  * as the editor field.
33606  * @constructor
33607  * @param {Object} config (used to be the tree panel.)
33608  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33609  * 
33610  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
33611  * @cfg {Roo.form.TextField|Object} field The field configuration
33612  *
33613  * 
33614  */
33615 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
33616     var tree = config;
33617     var field;
33618     if (oldconfig) { // old style..
33619         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
33620     } else {
33621         // new style..
33622         tree = config.tree;
33623         config.field = config.field  || {};
33624         config.field.xtype = 'TextField';
33625         field = Roo.factory(config.field, Roo.form);
33626     }
33627     config = config || {};
33628     
33629     
33630     this.addEvents({
33631         /**
33632          * @event beforenodeedit
33633          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33634          * false from the handler of this event.
33635          * @param {Editor} this
33636          * @param {Roo.tree.Node} node 
33637          */
33638         "beforenodeedit" : true
33639     });
33640     
33641     //Roo.log(config);
33642     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
33643
33644     this.tree = tree;
33645
33646     tree.on('beforeclick', this.beforeNodeClick, this);
33647     tree.getTreeEl().on('mousedown', this.hide, this);
33648     this.on('complete', this.updateNode, this);
33649     this.on('beforestartedit', this.fitToTree, this);
33650     this.on('startedit', this.bindScroll, this, {delay:10});
33651     this.on('specialkey', this.onSpecialKey, this);
33652 };
33653
33654 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33655     /**
33656      * @cfg {String} alignment
33657      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33658      */
33659     alignment: "l-l",
33660     // inherit
33661     autoSize: false,
33662     /**
33663      * @cfg {Boolean} hideEl
33664      * True to hide the bound element while the editor is displayed (defaults to false)
33665      */
33666     hideEl : false,
33667     /**
33668      * @cfg {String} cls
33669      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33670      */
33671     cls: "x-small-editor x-tree-editor",
33672     /**
33673      * @cfg {Boolean} shim
33674      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33675      */
33676     shim:false,
33677     // inherit
33678     shadow:"frame",
33679     /**
33680      * @cfg {Number} maxWidth
33681      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33682      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33683      * scroll and client offsets into account prior to each edit.
33684      */
33685     maxWidth: 250,
33686
33687     editDelay : 350,
33688
33689     // private
33690     fitToTree : function(ed, el){
33691         var td = this.tree.getTreeEl().dom, nd = el.dom;
33692         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33693             td.scrollLeft = nd.offsetLeft;
33694         }
33695         var w = Math.min(
33696                 this.maxWidth,
33697                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33698         this.setSize(w, '');
33699         
33700         return this.fireEvent('beforenodeedit', this, this.editNode);
33701         
33702     },
33703
33704     // private
33705     triggerEdit : function(node){
33706         this.completeEdit();
33707         this.editNode = node;
33708         this.startEdit(node.ui.textNode, node.text);
33709     },
33710
33711     // private
33712     bindScroll : function(){
33713         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33714     },
33715
33716     // private
33717     beforeNodeClick : function(node, e){
33718         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33719         this.lastClick = new Date();
33720         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33721             e.stopEvent();
33722             this.triggerEdit(node);
33723             return false;
33724         }
33725         return true;
33726     },
33727
33728     // private
33729     updateNode : function(ed, value){
33730         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33731         this.editNode.setText(value);
33732     },
33733
33734     // private
33735     onHide : function(){
33736         Roo.tree.TreeEditor.superclass.onHide.call(this);
33737         if(this.editNode){
33738             this.editNode.ui.focus();
33739         }
33740     },
33741
33742     // private
33743     onSpecialKey : function(field, e){
33744         var k = e.getKey();
33745         if(k == e.ESC){
33746             e.stopEvent();
33747             this.cancelEdit();
33748         }else if(k == e.ENTER && !e.hasModifier()){
33749             e.stopEvent();
33750             this.completeEdit();
33751         }
33752     }
33753 });//<Script type="text/javascript">
33754 /*
33755  * Based on:
33756  * Ext JS Library 1.1.1
33757  * Copyright(c) 2006-2007, Ext JS, LLC.
33758  *
33759  * Originally Released Under LGPL - original licence link has changed is not relivant.
33760  *
33761  * Fork - LGPL
33762  * <script type="text/javascript">
33763  */
33764  
33765 /**
33766  * Not documented??? - probably should be...
33767  */
33768
33769 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33770     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33771     
33772     renderElements : function(n, a, targetNode, bulkRender){
33773         //consel.log("renderElements?");
33774         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33775
33776         var t = n.getOwnerTree();
33777         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33778         
33779         var cols = t.columns;
33780         var bw = t.borderWidth;
33781         var c = cols[0];
33782         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33783          var cb = typeof a.checked == "boolean";
33784         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33785         var colcls = 'x-t-' + tid + '-c0';
33786         var buf = [
33787             '<li class="x-tree-node">',
33788             
33789                 
33790                 '<div class="x-tree-node-el ', a.cls,'">',
33791                     // extran...
33792                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33793                 
33794                 
33795                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33796                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33797                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33798                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33799                            (a.iconCls ? ' '+a.iconCls : ''),
33800                            '" unselectable="on" />',
33801                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33802                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33803                              
33804                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33805                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33806                             '<span unselectable="on" qtip="' + tx + '">',
33807                              tx,
33808                              '</span></a>' ,
33809                     '</div>',
33810                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33811                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33812                  ];
33813         for(var i = 1, len = cols.length; i < len; i++){
33814             c = cols[i];
33815             colcls = 'x-t-' + tid + '-c' +i;
33816             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33817             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33818                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33819                       "</div>");
33820          }
33821          
33822          buf.push(
33823             '</a>',
33824             '<div class="x-clear"></div></div>',
33825             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33826             "</li>");
33827         
33828         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33829             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33830                                 n.nextSibling.ui.getEl(), buf.join(""));
33831         }else{
33832             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33833         }
33834         var el = this.wrap.firstChild;
33835         this.elRow = el;
33836         this.elNode = el.firstChild;
33837         this.ranchor = el.childNodes[1];
33838         this.ctNode = this.wrap.childNodes[1];
33839         var cs = el.firstChild.childNodes;
33840         this.indentNode = cs[0];
33841         this.ecNode = cs[1];
33842         this.iconNode = cs[2];
33843         var index = 3;
33844         if(cb){
33845             this.checkbox = cs[3];
33846             index++;
33847         }
33848         this.anchor = cs[index];
33849         
33850         this.textNode = cs[index].firstChild;
33851         
33852         //el.on("click", this.onClick, this);
33853         //el.on("dblclick", this.onDblClick, this);
33854         
33855         
33856        // console.log(this);
33857     },
33858     initEvents : function(){
33859         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33860         
33861             
33862         var a = this.ranchor;
33863
33864         var el = Roo.get(a);
33865
33866         if(Roo.isOpera){ // opera render bug ignores the CSS
33867             el.setStyle("text-decoration", "none");
33868         }
33869
33870         el.on("click", this.onClick, this);
33871         el.on("dblclick", this.onDblClick, this);
33872         el.on("contextmenu", this.onContextMenu, this);
33873         
33874     },
33875     
33876     /*onSelectedChange : function(state){
33877         if(state){
33878             this.focus();
33879             this.addClass("x-tree-selected");
33880         }else{
33881             //this.blur();
33882             this.removeClass("x-tree-selected");
33883         }
33884     },*/
33885     addClass : function(cls){
33886         if(this.elRow){
33887             Roo.fly(this.elRow).addClass(cls);
33888         }
33889         
33890     },
33891     
33892     
33893     removeClass : function(cls){
33894         if(this.elRow){
33895             Roo.fly(this.elRow).removeClass(cls);
33896         }
33897     }
33898
33899     
33900     
33901 });//<Script type="text/javascript">
33902
33903 /*
33904  * Based on:
33905  * Ext JS Library 1.1.1
33906  * Copyright(c) 2006-2007, Ext JS, LLC.
33907  *
33908  * Originally Released Under LGPL - original licence link has changed is not relivant.
33909  *
33910  * Fork - LGPL
33911  * <script type="text/javascript">
33912  */
33913  
33914
33915 /**
33916  * @class Roo.tree.ColumnTree
33917  * @extends Roo.data.TreePanel
33918  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33919  * @cfg {int} borderWidth  compined right/left border allowance
33920  * @constructor
33921  * @param {String/HTMLElement/Element} el The container element
33922  * @param {Object} config
33923  */
33924 Roo.tree.ColumnTree =  function(el, config)
33925 {
33926    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33927    this.addEvents({
33928         /**
33929         * @event resize
33930         * Fire this event on a container when it resizes
33931         * @param {int} w Width
33932         * @param {int} h Height
33933         */
33934        "resize" : true
33935     });
33936     this.on('resize', this.onResize, this);
33937 };
33938
33939 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33940     //lines:false,
33941     
33942     
33943     borderWidth: Roo.isBorderBox ? 0 : 2, 
33944     headEls : false,
33945     
33946     render : function(){
33947         // add the header.....
33948        
33949         Roo.tree.ColumnTree.superclass.render.apply(this);
33950         
33951         this.el.addClass('x-column-tree');
33952         
33953         this.headers = this.el.createChild(
33954             {cls:'x-tree-headers'},this.innerCt.dom);
33955    
33956         var cols = this.columns, c;
33957         var totalWidth = 0;
33958         this.headEls = [];
33959         var  len = cols.length;
33960         for(var i = 0; i < len; i++){
33961              c = cols[i];
33962              totalWidth += c.width;
33963             this.headEls.push(this.headers.createChild({
33964                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33965                  cn: {
33966                      cls:'x-tree-hd-text',
33967                      html: c.header
33968                  },
33969                  style:'width:'+(c.width-this.borderWidth)+'px;'
33970              }));
33971         }
33972         this.headers.createChild({cls:'x-clear'});
33973         // prevent floats from wrapping when clipped
33974         this.headers.setWidth(totalWidth);
33975         //this.innerCt.setWidth(totalWidth);
33976         this.innerCt.setStyle({ overflow: 'auto' });
33977         this.onResize(this.width, this.height);
33978              
33979         
33980     },
33981     onResize : function(w,h)
33982     {
33983         this.height = h;
33984         this.width = w;
33985         // resize cols..
33986         this.innerCt.setWidth(this.width);
33987         this.innerCt.setHeight(this.height-20);
33988         
33989         // headers...
33990         var cols = this.columns, c;
33991         var totalWidth = 0;
33992         var expEl = false;
33993         var len = cols.length;
33994         for(var i = 0; i < len; i++){
33995             c = cols[i];
33996             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33997                 // it's the expander..
33998                 expEl  = this.headEls[i];
33999                 continue;
34000             }
34001             totalWidth += c.width;
34002             
34003         }
34004         if (expEl) {
34005             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
34006         }
34007         this.headers.setWidth(w-20);
34008
34009         
34010         
34011         
34012     }
34013 });
34014 /*
34015  * Based on:
34016  * Ext JS Library 1.1.1
34017  * Copyright(c) 2006-2007, Ext JS, LLC.
34018  *
34019  * Originally Released Under LGPL - original licence link has changed is not relivant.
34020  *
34021  * Fork - LGPL
34022  * <script type="text/javascript">
34023  */
34024  
34025 /**
34026  * @class Roo.menu.Menu
34027  * @extends Roo.util.Observable
34028  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
34029  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
34030  * @constructor
34031  * Creates a new Menu
34032  * @param {Object} config Configuration options
34033  */
34034 Roo.menu.Menu = function(config){
34035     Roo.apply(this, config);
34036     this.id = this.id || Roo.id();
34037     this.addEvents({
34038         /**
34039          * @event beforeshow
34040          * Fires before this menu is displayed
34041          * @param {Roo.menu.Menu} this
34042          */
34043         beforeshow : true,
34044         /**
34045          * @event beforehide
34046          * Fires before this menu is hidden
34047          * @param {Roo.menu.Menu} this
34048          */
34049         beforehide : true,
34050         /**
34051          * @event show
34052          * Fires after this menu is displayed
34053          * @param {Roo.menu.Menu} this
34054          */
34055         show : true,
34056         /**
34057          * @event hide
34058          * Fires after this menu is hidden
34059          * @param {Roo.menu.Menu} this
34060          */
34061         hide : true,
34062         /**
34063          * @event click
34064          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
34065          * @param {Roo.menu.Menu} this
34066          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34067          * @param {Roo.EventObject} e
34068          */
34069         click : true,
34070         /**
34071          * @event mouseover
34072          * Fires when the mouse is hovering over this menu
34073          * @param {Roo.menu.Menu} this
34074          * @param {Roo.EventObject} e
34075          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34076          */
34077         mouseover : true,
34078         /**
34079          * @event mouseout
34080          * Fires when the mouse exits this menu
34081          * @param {Roo.menu.Menu} this
34082          * @param {Roo.EventObject} e
34083          * @param {Roo.menu.Item} menuItem The menu item that was clicked
34084          */
34085         mouseout : true,
34086         /**
34087          * @event itemclick
34088          * Fires when a menu item contained in this menu is clicked
34089          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
34090          * @param {Roo.EventObject} e
34091          */
34092         itemclick: true
34093     });
34094     if (this.registerMenu) {
34095         Roo.menu.MenuMgr.register(this);
34096     }
34097     
34098     var mis = this.items;
34099     this.items = new Roo.util.MixedCollection();
34100     if(mis){
34101         this.add.apply(this, mis);
34102     }
34103 };
34104
34105 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
34106     /**
34107      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
34108      */
34109     minWidth : 120,
34110     /**
34111      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
34112      * for bottom-right shadow (defaults to "sides")
34113      */
34114     shadow : "sides",
34115     /**
34116      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
34117      * this menu (defaults to "tl-tr?")
34118      */
34119     subMenuAlign : "tl-tr?",
34120     /**
34121      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
34122      * relative to its element of origin (defaults to "tl-bl?")
34123      */
34124     defaultAlign : "tl-bl?",
34125     /**
34126      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
34127      */
34128     allowOtherMenus : false,
34129     /**
34130      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
34131      */
34132     registerMenu : true,
34133
34134     hidden:true,
34135
34136     // private
34137     render : function(){
34138         if(this.el){
34139             return;
34140         }
34141         var el = this.el = new Roo.Layer({
34142             cls: "x-menu",
34143             shadow:this.shadow,
34144             constrain: false,
34145             parentEl: this.parentEl || document.body,
34146             zindex:15000
34147         });
34148
34149         this.keyNav = new Roo.menu.MenuNav(this);
34150
34151         if(this.plain){
34152             el.addClass("x-menu-plain");
34153         }
34154         if(this.cls){
34155             el.addClass(this.cls);
34156         }
34157         // generic focus element
34158         this.focusEl = el.createChild({
34159             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
34160         });
34161         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
34162         ul.on("click", this.onClick, this);
34163         ul.on("mouseover", this.onMouseOver, this);
34164         ul.on("mouseout", this.onMouseOut, this);
34165         this.items.each(function(item){
34166             var li = document.createElement("li");
34167             li.className = "x-menu-list-item";
34168             ul.dom.appendChild(li);
34169             item.render(li, this);
34170         }, this);
34171         this.ul = ul;
34172         this.autoWidth();
34173     },
34174
34175     // private
34176     autoWidth : function(){
34177         var el = this.el, ul = this.ul;
34178         if(!el){
34179             return;
34180         }
34181         var w = this.width;
34182         if(w){
34183             el.setWidth(w);
34184         }else if(Roo.isIE){
34185             el.setWidth(this.minWidth);
34186             var t = el.dom.offsetWidth; // force recalc
34187             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
34188         }
34189     },
34190
34191     // private
34192     delayAutoWidth : function(){
34193         if(this.rendered){
34194             if(!this.awTask){
34195                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
34196             }
34197             this.awTask.delay(20);
34198         }
34199     },
34200
34201     // private
34202     findTargetItem : function(e){
34203         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
34204         if(t && t.menuItemId){
34205             return this.items.get(t.menuItemId);
34206         }
34207     },
34208
34209     // private
34210     onClick : function(e){
34211         var t;
34212         if(t = this.findTargetItem(e)){
34213             t.onClick(e);
34214             this.fireEvent("click", this, t, e);
34215         }
34216     },
34217
34218     // private
34219     setActiveItem : function(item, autoExpand){
34220         if(item != this.activeItem){
34221             if(this.activeItem){
34222                 this.activeItem.deactivate();
34223             }
34224             this.activeItem = item;
34225             item.activate(autoExpand);
34226         }else if(autoExpand){
34227             item.expandMenu();
34228         }
34229     },
34230
34231     // private
34232     tryActivate : function(start, step){
34233         var items = this.items;
34234         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
34235             var item = items.get(i);
34236             if(!item.disabled && item.canActivate){
34237                 this.setActiveItem(item, false);
34238                 return item;
34239             }
34240         }
34241         return false;
34242     },
34243
34244     // private
34245     onMouseOver : function(e){
34246         var t;
34247         if(t = this.findTargetItem(e)){
34248             if(t.canActivate && !t.disabled){
34249                 this.setActiveItem(t, true);
34250             }
34251         }
34252         this.fireEvent("mouseover", this, e, t);
34253     },
34254
34255     // private
34256     onMouseOut : function(e){
34257         var t;
34258         if(t = this.findTargetItem(e)){
34259             if(t == this.activeItem && t.shouldDeactivate(e)){
34260                 this.activeItem.deactivate();
34261                 delete this.activeItem;
34262             }
34263         }
34264         this.fireEvent("mouseout", this, e, t);
34265     },
34266
34267     /**
34268      * Read-only.  Returns true if the menu is currently displayed, else false.
34269      * @type Boolean
34270      */
34271     isVisible : function(){
34272         return this.el && !this.hidden;
34273     },
34274
34275     /**
34276      * Displays this menu relative to another element
34277      * @param {String/HTMLElement/Roo.Element} element The element to align to
34278      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
34279      * the element (defaults to this.defaultAlign)
34280      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34281      */
34282     show : function(el, pos, parentMenu){
34283         this.parentMenu = parentMenu;
34284         if(!this.el){
34285             this.render();
34286         }
34287         this.fireEvent("beforeshow", this);
34288         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
34289     },
34290
34291     /**
34292      * Displays this menu at a specific xy position
34293      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
34294      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
34295      */
34296     showAt : function(xy, parentMenu, /* private: */_e){
34297         this.parentMenu = parentMenu;
34298         if(!this.el){
34299             this.render();
34300         }
34301         if(_e !== false){
34302             this.fireEvent("beforeshow", this);
34303             xy = this.el.adjustForConstraints(xy);
34304         }
34305         this.el.setXY(xy);
34306         this.el.show();
34307         this.hidden = false;
34308         this.focus();
34309         this.fireEvent("show", this);
34310     },
34311
34312     focus : function(){
34313         if(!this.hidden){
34314             this.doFocus.defer(50, this);
34315         }
34316     },
34317
34318     doFocus : function(){
34319         if(!this.hidden){
34320             this.focusEl.focus();
34321         }
34322     },
34323
34324     /**
34325      * Hides this menu and optionally all parent menus
34326      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34327      */
34328     hide : function(deep){
34329         if(this.el && this.isVisible()){
34330             this.fireEvent("beforehide", this);
34331             if(this.activeItem){
34332                 this.activeItem.deactivate();
34333                 this.activeItem = null;
34334             }
34335             this.el.hide();
34336             this.hidden = true;
34337             this.fireEvent("hide", this);
34338         }
34339         if(deep === true && this.parentMenu){
34340             this.parentMenu.hide(true);
34341         }
34342     },
34343
34344     /**
34345      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34346      * Any of the following are valid:
34347      * <ul>
34348      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34349      * <li>An HTMLElement object which will be converted to a menu item</li>
34350      * <li>A menu item config object that will be created as a new menu item</li>
34351      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34352      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34353      * </ul>
34354      * Usage:
34355      * <pre><code>
34356 // Create the menu
34357 var menu = new Roo.menu.Menu();
34358
34359 // Create a menu item to add by reference
34360 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34361
34362 // Add a bunch of items at once using different methods.
34363 // Only the last item added will be returned.
34364 var item = menu.add(
34365     menuItem,                // add existing item by ref
34366     'Dynamic Item',          // new TextItem
34367     '-',                     // new separator
34368     { text: 'Config Item' }  // new item by config
34369 );
34370 </code></pre>
34371      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34372      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34373      */
34374     add : function(){
34375         var a = arguments, l = a.length, item;
34376         for(var i = 0; i < l; i++){
34377             var el = a[i];
34378             if ((typeof(el) == "object") && el.xtype && el.xns) {
34379                 el = Roo.factory(el, Roo.menu);
34380             }
34381             
34382             if(el.render){ // some kind of Item
34383                 item = this.addItem(el);
34384             }else if(typeof el == "string"){ // string
34385                 if(el == "separator" || el == "-"){
34386                     item = this.addSeparator();
34387                 }else{
34388                     item = this.addText(el);
34389                 }
34390             }else if(el.tagName || el.el){ // element
34391                 item = this.addElement(el);
34392             }else if(typeof el == "object"){ // must be menu item config?
34393                 item = this.addMenuItem(el);
34394             }
34395         }
34396         return item;
34397     },
34398
34399     /**
34400      * Returns this menu's underlying {@link Roo.Element} object
34401      * @return {Roo.Element} The element
34402      */
34403     getEl : function(){
34404         if(!this.el){
34405             this.render();
34406         }
34407         return this.el;
34408     },
34409
34410     /**
34411      * Adds a separator bar to the menu
34412      * @return {Roo.menu.Item} The menu item that was added
34413      */
34414     addSeparator : function(){
34415         return this.addItem(new Roo.menu.Separator());
34416     },
34417
34418     /**
34419      * Adds an {@link Roo.Element} object to the menu
34420      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34421      * @return {Roo.menu.Item} The menu item that was added
34422      */
34423     addElement : function(el){
34424         return this.addItem(new Roo.menu.BaseItem(el));
34425     },
34426
34427     /**
34428      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34429      * @param {Roo.menu.Item} item The menu item to add
34430      * @return {Roo.menu.Item} The menu item that was added
34431      */
34432     addItem : function(item){
34433         this.items.add(item);
34434         if(this.ul){
34435             var li = document.createElement("li");
34436             li.className = "x-menu-list-item";
34437             this.ul.dom.appendChild(li);
34438             item.render(li, this);
34439             this.delayAutoWidth();
34440         }
34441         return item;
34442     },
34443
34444     /**
34445      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34446      * @param {Object} config A MenuItem config object
34447      * @return {Roo.menu.Item} The menu item that was added
34448      */
34449     addMenuItem : function(config){
34450         if(!(config instanceof Roo.menu.Item)){
34451             if(typeof config.checked == "boolean"){ // must be check menu item config?
34452                 config = new Roo.menu.CheckItem(config);
34453             }else{
34454                 config = new Roo.menu.Item(config);
34455             }
34456         }
34457         return this.addItem(config);
34458     },
34459
34460     /**
34461      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34462      * @param {String} text The text to display in the menu item
34463      * @return {Roo.menu.Item} The menu item that was added
34464      */
34465     addText : function(text){
34466         return this.addItem(new Roo.menu.TextItem({ text : text }));
34467     },
34468
34469     /**
34470      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34471      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34472      * @param {Roo.menu.Item} item The menu item to add
34473      * @return {Roo.menu.Item} The menu item that was added
34474      */
34475     insert : function(index, item){
34476         this.items.insert(index, item);
34477         if(this.ul){
34478             var li = document.createElement("li");
34479             li.className = "x-menu-list-item";
34480             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34481             item.render(li, this);
34482             this.delayAutoWidth();
34483         }
34484         return item;
34485     },
34486
34487     /**
34488      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34489      * @param {Roo.menu.Item} item The menu item to remove
34490      */
34491     remove : function(item){
34492         this.items.removeKey(item.id);
34493         item.destroy();
34494     },
34495
34496     /**
34497      * Removes and destroys all items in the menu
34498      */
34499     removeAll : function(){
34500         var f;
34501         while(f = this.items.first()){
34502             this.remove(f);
34503         }
34504     }
34505 });
34506
34507 // MenuNav is a private utility class used internally by the Menu
34508 Roo.menu.MenuNav = function(menu){
34509     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34510     this.scope = this.menu = menu;
34511 };
34512
34513 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34514     doRelay : function(e, h){
34515         var k = e.getKey();
34516         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34517             this.menu.tryActivate(0, 1);
34518             return false;
34519         }
34520         return h.call(this.scope || this, e, this.menu);
34521     },
34522
34523     up : function(e, m){
34524         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34525             m.tryActivate(m.items.length-1, -1);
34526         }
34527     },
34528
34529     down : function(e, m){
34530         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34531             m.tryActivate(0, 1);
34532         }
34533     },
34534
34535     right : function(e, m){
34536         if(m.activeItem){
34537             m.activeItem.expandMenu(true);
34538         }
34539     },
34540
34541     left : function(e, m){
34542         m.hide();
34543         if(m.parentMenu && m.parentMenu.activeItem){
34544             m.parentMenu.activeItem.activate();
34545         }
34546     },
34547
34548     enter : function(e, m){
34549         if(m.activeItem){
34550             e.stopPropagation();
34551             m.activeItem.onClick(e);
34552             m.fireEvent("click", this, m.activeItem);
34553             return true;
34554         }
34555     }
34556 });/*
34557  * Based on:
34558  * Ext JS Library 1.1.1
34559  * Copyright(c) 2006-2007, Ext JS, LLC.
34560  *
34561  * Originally Released Under LGPL - original licence link has changed is not relivant.
34562  *
34563  * Fork - LGPL
34564  * <script type="text/javascript">
34565  */
34566  
34567 /**
34568  * @class Roo.menu.MenuMgr
34569  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34570  * @singleton
34571  */
34572 Roo.menu.MenuMgr = function(){
34573    var menus, active, groups = {}, attached = false, lastShow = new Date();
34574
34575    // private - called when first menu is created
34576    function init(){
34577        menus = {};
34578        active = new Roo.util.MixedCollection();
34579        Roo.get(document).addKeyListener(27, function(){
34580            if(active.length > 0){
34581                hideAll();
34582            }
34583        });
34584    }
34585
34586    // private
34587    function hideAll(){
34588        if(active && active.length > 0){
34589            var c = active.clone();
34590            c.each(function(m){
34591                m.hide();
34592            });
34593        }
34594    }
34595
34596    // private
34597    function onHide(m){
34598        active.remove(m);
34599        if(active.length < 1){
34600            Roo.get(document).un("mousedown", onMouseDown);
34601            attached = false;
34602        }
34603    }
34604
34605    // private
34606    function onShow(m){
34607        var last = active.last();
34608        lastShow = new Date();
34609        active.add(m);
34610        if(!attached){
34611            Roo.get(document).on("mousedown", onMouseDown);
34612            attached = true;
34613        }
34614        if(m.parentMenu){
34615           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34616           m.parentMenu.activeChild = m;
34617        }else if(last && last.isVisible()){
34618           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34619        }
34620    }
34621
34622    // private
34623    function onBeforeHide(m){
34624        if(m.activeChild){
34625            m.activeChild.hide();
34626        }
34627        if(m.autoHideTimer){
34628            clearTimeout(m.autoHideTimer);
34629            delete m.autoHideTimer;
34630        }
34631    }
34632
34633    // private
34634    function onBeforeShow(m){
34635        var pm = m.parentMenu;
34636        if(!pm && !m.allowOtherMenus){
34637            hideAll();
34638        }else if(pm && pm.activeChild && active != m){
34639            pm.activeChild.hide();
34640        }
34641    }
34642
34643    // private
34644    function onMouseDown(e){
34645        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34646            hideAll();
34647        }
34648    }
34649
34650    // private
34651    function onBeforeCheck(mi, state){
34652        if(state){
34653            var g = groups[mi.group];
34654            for(var i = 0, l = g.length; i < l; i++){
34655                if(g[i] != mi){
34656                    g[i].setChecked(false);
34657                }
34658            }
34659        }
34660    }
34661
34662    return {
34663
34664        /**
34665         * Hides all menus that are currently visible
34666         */
34667        hideAll : function(){
34668             hideAll();  
34669        },
34670
34671        // private
34672        register : function(menu){
34673            if(!menus){
34674                init();
34675            }
34676            menus[menu.id] = menu;
34677            menu.on("beforehide", onBeforeHide);
34678            menu.on("hide", onHide);
34679            menu.on("beforeshow", onBeforeShow);
34680            menu.on("show", onShow);
34681            var g = menu.group;
34682            if(g && menu.events["checkchange"]){
34683                if(!groups[g]){
34684                    groups[g] = [];
34685                }
34686                groups[g].push(menu);
34687                menu.on("checkchange", onCheck);
34688            }
34689        },
34690
34691         /**
34692          * Returns a {@link Roo.menu.Menu} object
34693          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34694          * be used to generate and return a new Menu instance.
34695          */
34696        get : function(menu){
34697            if(typeof menu == "string"){ // menu id
34698                return menus[menu];
34699            }else if(menu.events){  // menu instance
34700                return menu;
34701            }else if(typeof menu.length == 'number'){ // array of menu items?
34702                return new Roo.menu.Menu({items:menu});
34703            }else{ // otherwise, must be a config
34704                return new Roo.menu.Menu(menu);
34705            }
34706        },
34707
34708        // private
34709        unregister : function(menu){
34710            delete menus[menu.id];
34711            menu.un("beforehide", onBeforeHide);
34712            menu.un("hide", onHide);
34713            menu.un("beforeshow", onBeforeShow);
34714            menu.un("show", onShow);
34715            var g = menu.group;
34716            if(g && menu.events["checkchange"]){
34717                groups[g].remove(menu);
34718                menu.un("checkchange", onCheck);
34719            }
34720        },
34721
34722        // private
34723        registerCheckable : function(menuItem){
34724            var g = menuItem.group;
34725            if(g){
34726                if(!groups[g]){
34727                    groups[g] = [];
34728                }
34729                groups[g].push(menuItem);
34730                menuItem.on("beforecheckchange", onBeforeCheck);
34731            }
34732        },
34733
34734        // private
34735        unregisterCheckable : function(menuItem){
34736            var g = menuItem.group;
34737            if(g){
34738                groups[g].remove(menuItem);
34739                menuItem.un("beforecheckchange", onBeforeCheck);
34740            }
34741        }
34742    };
34743 }();/*
34744  * Based on:
34745  * Ext JS Library 1.1.1
34746  * Copyright(c) 2006-2007, Ext JS, LLC.
34747  *
34748  * Originally Released Under LGPL - original licence link has changed is not relivant.
34749  *
34750  * Fork - LGPL
34751  * <script type="text/javascript">
34752  */
34753  
34754
34755 /**
34756  * @class Roo.menu.BaseItem
34757  * @extends Roo.Component
34758  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34759  * management and base configuration options shared by all menu components.
34760  * @constructor
34761  * Creates a new BaseItem
34762  * @param {Object} config Configuration options
34763  */
34764 Roo.menu.BaseItem = function(config){
34765     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34766
34767     this.addEvents({
34768         /**
34769          * @event click
34770          * Fires when this item is clicked
34771          * @param {Roo.menu.BaseItem} this
34772          * @param {Roo.EventObject} e
34773          */
34774         click: true,
34775         /**
34776          * @event activate
34777          * Fires when this item is activated
34778          * @param {Roo.menu.BaseItem} this
34779          */
34780         activate : true,
34781         /**
34782          * @event deactivate
34783          * Fires when this item is deactivated
34784          * @param {Roo.menu.BaseItem} this
34785          */
34786         deactivate : true
34787     });
34788
34789     if(this.handler){
34790         this.on("click", this.handler, this.scope, true);
34791     }
34792 };
34793
34794 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34795     /**
34796      * @cfg {Function} handler
34797      * A function that will handle the click event of this menu item (defaults to undefined)
34798      */
34799     /**
34800      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34801      */
34802     canActivate : false,
34803     /**
34804      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34805      */
34806     activeClass : "x-menu-item-active",
34807     /**
34808      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34809      */
34810     hideOnClick : true,
34811     /**
34812      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34813      */
34814     hideDelay : 100,
34815
34816     // private
34817     ctype: "Roo.menu.BaseItem",
34818
34819     // private
34820     actionMode : "container",
34821
34822     // private
34823     render : function(container, parentMenu){
34824         this.parentMenu = parentMenu;
34825         Roo.menu.BaseItem.superclass.render.call(this, container);
34826         this.container.menuItemId = this.id;
34827     },
34828
34829     // private
34830     onRender : function(container, position){
34831         this.el = Roo.get(this.el);
34832         container.dom.appendChild(this.el.dom);
34833     },
34834
34835     // private
34836     onClick : function(e){
34837         if(!this.disabled && this.fireEvent("click", this, e) !== false
34838                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34839             this.handleClick(e);
34840         }else{
34841             e.stopEvent();
34842         }
34843     },
34844
34845     // private
34846     activate : function(){
34847         if(this.disabled){
34848             return false;
34849         }
34850         var li = this.container;
34851         li.addClass(this.activeClass);
34852         this.region = li.getRegion().adjust(2, 2, -2, -2);
34853         this.fireEvent("activate", this);
34854         return true;
34855     },
34856
34857     // private
34858     deactivate : function(){
34859         this.container.removeClass(this.activeClass);
34860         this.fireEvent("deactivate", this);
34861     },
34862
34863     // private
34864     shouldDeactivate : function(e){
34865         return !this.region || !this.region.contains(e.getPoint());
34866     },
34867
34868     // private
34869     handleClick : function(e){
34870         if(this.hideOnClick){
34871             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34872         }
34873     },
34874
34875     // private
34876     expandMenu : function(autoActivate){
34877         // do nothing
34878     },
34879
34880     // private
34881     hideMenu : function(){
34882         // do nothing
34883     }
34884 });/*
34885  * Based on:
34886  * Ext JS Library 1.1.1
34887  * Copyright(c) 2006-2007, Ext JS, LLC.
34888  *
34889  * Originally Released Under LGPL - original licence link has changed is not relivant.
34890  *
34891  * Fork - LGPL
34892  * <script type="text/javascript">
34893  */
34894  
34895 /**
34896  * @class Roo.menu.Adapter
34897  * @extends Roo.menu.BaseItem
34898  * 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.
34899  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34900  * @constructor
34901  * Creates a new Adapter
34902  * @param {Object} config Configuration options
34903  */
34904 Roo.menu.Adapter = function(component, config){
34905     Roo.menu.Adapter.superclass.constructor.call(this, config);
34906     this.component = component;
34907 };
34908 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34909     // private
34910     canActivate : true,
34911
34912     // private
34913     onRender : function(container, position){
34914         this.component.render(container);
34915         this.el = this.component.getEl();
34916     },
34917
34918     // private
34919     activate : function(){
34920         if(this.disabled){
34921             return false;
34922         }
34923         this.component.focus();
34924         this.fireEvent("activate", this);
34925         return true;
34926     },
34927
34928     // private
34929     deactivate : function(){
34930         this.fireEvent("deactivate", this);
34931     },
34932
34933     // private
34934     disable : function(){
34935         this.component.disable();
34936         Roo.menu.Adapter.superclass.disable.call(this);
34937     },
34938
34939     // private
34940     enable : function(){
34941         this.component.enable();
34942         Roo.menu.Adapter.superclass.enable.call(this);
34943     }
34944 });/*
34945  * Based on:
34946  * Ext JS Library 1.1.1
34947  * Copyright(c) 2006-2007, Ext JS, LLC.
34948  *
34949  * Originally Released Under LGPL - original licence link has changed is not relivant.
34950  *
34951  * Fork - LGPL
34952  * <script type="text/javascript">
34953  */
34954
34955 /**
34956  * @class Roo.menu.TextItem
34957  * @extends Roo.menu.BaseItem
34958  * Adds a static text string to a menu, usually used as either a heading or group separator.
34959  * Note: old style constructor with text is still supported.
34960  * 
34961  * @constructor
34962  * Creates a new TextItem
34963  * @param {Object} cfg Configuration
34964  */
34965 Roo.menu.TextItem = function(cfg){
34966     if (typeof(cfg) == 'string') {
34967         this.text = cfg;
34968     } else {
34969         Roo.apply(this,cfg);
34970     }
34971     
34972     Roo.menu.TextItem.superclass.constructor.call(this);
34973 };
34974
34975 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34976     /**
34977      * @cfg {Boolean} text Text to show on item.
34978      */
34979     text : '',
34980     
34981     /**
34982      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34983      */
34984     hideOnClick : false,
34985     /**
34986      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34987      */
34988     itemCls : "x-menu-text",
34989
34990     // private
34991     onRender : function(){
34992         var s = document.createElement("span");
34993         s.className = this.itemCls;
34994         s.innerHTML = this.text;
34995         this.el = s;
34996         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34997     }
34998 });/*
34999  * Based on:
35000  * Ext JS Library 1.1.1
35001  * Copyright(c) 2006-2007, Ext JS, LLC.
35002  *
35003  * Originally Released Under LGPL - original licence link has changed is not relivant.
35004  *
35005  * Fork - LGPL
35006  * <script type="text/javascript">
35007  */
35008
35009 /**
35010  * @class Roo.menu.Separator
35011  * @extends Roo.menu.BaseItem
35012  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
35013  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
35014  * @constructor
35015  * @param {Object} config Configuration options
35016  */
35017 Roo.menu.Separator = function(config){
35018     Roo.menu.Separator.superclass.constructor.call(this, config);
35019 };
35020
35021 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
35022     /**
35023      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
35024      */
35025     itemCls : "x-menu-sep",
35026     /**
35027      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
35028      */
35029     hideOnClick : false,
35030
35031     // private
35032     onRender : function(li){
35033         var s = document.createElement("span");
35034         s.className = this.itemCls;
35035         s.innerHTML = "&#160;";
35036         this.el = s;
35037         li.addClass("x-menu-sep-li");
35038         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
35039     }
35040 });/*
35041  * Based on:
35042  * Ext JS Library 1.1.1
35043  * Copyright(c) 2006-2007, Ext JS, LLC.
35044  *
35045  * Originally Released Under LGPL - original licence link has changed is not relivant.
35046  *
35047  * Fork - LGPL
35048  * <script type="text/javascript">
35049  */
35050 /**
35051  * @class Roo.menu.Item
35052  * @extends Roo.menu.BaseItem
35053  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
35054  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
35055  * activation and click handling.
35056  * @constructor
35057  * Creates a new Item
35058  * @param {Object} config Configuration options
35059  */
35060 Roo.menu.Item = function(config){
35061     Roo.menu.Item.superclass.constructor.call(this, config);
35062     if(this.menu){
35063         this.menu = Roo.menu.MenuMgr.get(this.menu);
35064     }
35065 };
35066 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
35067     
35068     /**
35069      * @cfg {String} text
35070      * The text to show on the menu item.
35071      */
35072     text: '',
35073      /**
35074      * @cfg {String} HTML to render in menu
35075      * The text to show on the menu item (HTML version).
35076      */
35077     html: '',
35078     /**
35079      * @cfg {String} icon
35080      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
35081      */
35082     icon: undefined,
35083     /**
35084      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
35085      */
35086     itemCls : "x-menu-item",
35087     /**
35088      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
35089      */
35090     canActivate : true,
35091     /**
35092      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
35093      */
35094     showDelay: 200,
35095     // doc'd in BaseItem
35096     hideDelay: 200,
35097
35098     // private
35099     ctype: "Roo.menu.Item",
35100     
35101     // private
35102     onRender : function(container, position){
35103         var el = document.createElement("a");
35104         el.hideFocus = true;
35105         el.unselectable = "on";
35106         el.href = this.href || "#";
35107         if(this.hrefTarget){
35108             el.target = this.hrefTarget;
35109         }
35110         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
35111         
35112         var html = this.html.length ? this.html  : String.format('{0}',this.text);
35113         
35114         el.innerHTML = String.format(
35115                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
35116                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
35117         this.el = el;
35118         Roo.menu.Item.superclass.onRender.call(this, container, position);
35119     },
35120
35121     /**
35122      * Sets the text to display in this menu item
35123      * @param {String} text The text to display
35124      * @param {Boolean} isHTML true to indicate text is pure html.
35125      */
35126     setText : function(text, isHTML){
35127         if (isHTML) {
35128             this.html = text;
35129         } else {
35130             this.text = text;
35131             this.html = '';
35132         }
35133         if(this.rendered){
35134             var html = this.html.length ? this.html  : String.format('{0}',this.text);
35135      
35136             this.el.update(String.format(
35137                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
35138                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
35139             this.parentMenu.autoWidth();
35140         }
35141     },
35142
35143     // private
35144     handleClick : function(e){
35145         if(!this.href){ // if no link defined, stop the event automatically
35146             e.stopEvent();
35147         }
35148         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
35149     },
35150
35151     // private
35152     activate : function(autoExpand){
35153         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
35154             this.focus();
35155             if(autoExpand){
35156                 this.expandMenu();
35157             }
35158         }
35159         return true;
35160     },
35161
35162     // private
35163     shouldDeactivate : function(e){
35164         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
35165             if(this.menu && this.menu.isVisible()){
35166                 return !this.menu.getEl().getRegion().contains(e.getPoint());
35167             }
35168             return true;
35169         }
35170         return false;
35171     },
35172
35173     // private
35174     deactivate : function(){
35175         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
35176         this.hideMenu();
35177     },
35178
35179     // private
35180     expandMenu : function(autoActivate){
35181         if(!this.disabled && this.menu){
35182             clearTimeout(this.hideTimer);
35183             delete this.hideTimer;
35184             if(!this.menu.isVisible() && !this.showTimer){
35185                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
35186             }else if (this.menu.isVisible() && autoActivate){
35187                 this.menu.tryActivate(0, 1);
35188             }
35189         }
35190     },
35191
35192     // private
35193     deferExpand : function(autoActivate){
35194         delete this.showTimer;
35195         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
35196         if(autoActivate){
35197             this.menu.tryActivate(0, 1);
35198         }
35199     },
35200
35201     // private
35202     hideMenu : function(){
35203         clearTimeout(this.showTimer);
35204         delete this.showTimer;
35205         if(!this.hideTimer && this.menu && this.menu.isVisible()){
35206             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
35207         }
35208     },
35209
35210     // private
35211     deferHide : function(){
35212         delete this.hideTimer;
35213         this.menu.hide();
35214     }
35215 });/*
35216  * Based on:
35217  * Ext JS Library 1.1.1
35218  * Copyright(c) 2006-2007, Ext JS, LLC.
35219  *
35220  * Originally Released Under LGPL - original licence link has changed is not relivant.
35221  *
35222  * Fork - LGPL
35223  * <script type="text/javascript">
35224  */
35225  
35226 /**
35227  * @class Roo.menu.CheckItem
35228  * @extends Roo.menu.Item
35229  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
35230  * @constructor
35231  * Creates a new CheckItem
35232  * @param {Object} config Configuration options
35233  */
35234 Roo.menu.CheckItem = function(config){
35235     Roo.menu.CheckItem.superclass.constructor.call(this, config);
35236     this.addEvents({
35237         /**
35238          * @event beforecheckchange
35239          * Fires before the checked value is set, providing an opportunity to cancel if needed
35240          * @param {Roo.menu.CheckItem} this
35241          * @param {Boolean} checked The new checked value that will be set
35242          */
35243         "beforecheckchange" : true,
35244         /**
35245          * @event checkchange
35246          * Fires after the checked value has been set
35247          * @param {Roo.menu.CheckItem} this
35248          * @param {Boolean} checked The checked value that was set
35249          */
35250         "checkchange" : true
35251     });
35252     if(this.checkHandler){
35253         this.on('checkchange', this.checkHandler, this.scope);
35254     }
35255 };
35256 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
35257     /**
35258      * @cfg {String} group
35259      * All check items with the same group name will automatically be grouped into a single-select
35260      * radio button group (defaults to '')
35261      */
35262     /**
35263      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
35264      */
35265     itemCls : "x-menu-item x-menu-check-item",
35266     /**
35267      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
35268      */
35269     groupClass : "x-menu-group-item",
35270
35271     /**
35272      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
35273      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
35274      * initialized with checked = true will be rendered as checked.
35275      */
35276     checked: false,
35277
35278     // private
35279     ctype: "Roo.menu.CheckItem",
35280
35281     // private
35282     onRender : function(c){
35283         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
35284         if(this.group){
35285             this.el.addClass(this.groupClass);
35286         }
35287         Roo.menu.MenuMgr.registerCheckable(this);
35288         if(this.checked){
35289             this.checked = false;
35290             this.setChecked(true, true);
35291         }
35292     },
35293
35294     // private
35295     destroy : function(){
35296         if(this.rendered){
35297             Roo.menu.MenuMgr.unregisterCheckable(this);
35298         }
35299         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
35300     },
35301
35302     /**
35303      * Set the checked state of this item
35304      * @param {Boolean} checked The new checked value
35305      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35306      */
35307     setChecked : function(state, suppressEvent){
35308         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35309             if(this.container){
35310                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35311             }
35312             this.checked = state;
35313             if(suppressEvent !== true){
35314                 this.fireEvent("checkchange", this, state);
35315             }
35316         }
35317     },
35318
35319     // private
35320     handleClick : function(e){
35321        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35322            this.setChecked(!this.checked);
35323        }
35324        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35325     }
35326 });/*
35327  * Based on:
35328  * Ext JS Library 1.1.1
35329  * Copyright(c) 2006-2007, Ext JS, LLC.
35330  *
35331  * Originally Released Under LGPL - original licence link has changed is not relivant.
35332  *
35333  * Fork - LGPL
35334  * <script type="text/javascript">
35335  */
35336  
35337 /**
35338  * @class Roo.menu.DateItem
35339  * @extends Roo.menu.Adapter
35340  * A menu item that wraps the {@link Roo.DatPicker} component.
35341  * @constructor
35342  * Creates a new DateItem
35343  * @param {Object} config Configuration options
35344  */
35345 Roo.menu.DateItem = function(config){
35346     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35347     /** The Roo.DatePicker object @type Roo.DatePicker */
35348     this.picker = this.component;
35349     this.addEvents({select: true});
35350     
35351     this.picker.on("render", function(picker){
35352         picker.getEl().swallowEvent("click");
35353         picker.container.addClass("x-menu-date-item");
35354     });
35355
35356     this.picker.on("select", this.onSelect, this);
35357 };
35358
35359 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35360     // private
35361     onSelect : function(picker, date){
35362         this.fireEvent("select", this, date, picker);
35363         Roo.menu.DateItem.superclass.handleClick.call(this);
35364     }
35365 });/*
35366  * Based on:
35367  * Ext JS Library 1.1.1
35368  * Copyright(c) 2006-2007, Ext JS, LLC.
35369  *
35370  * Originally Released Under LGPL - original licence link has changed is not relivant.
35371  *
35372  * Fork - LGPL
35373  * <script type="text/javascript">
35374  */
35375  
35376 /**
35377  * @class Roo.menu.ColorItem
35378  * @extends Roo.menu.Adapter
35379  * A menu item that wraps the {@link Roo.ColorPalette} component.
35380  * @constructor
35381  * Creates a new ColorItem
35382  * @param {Object} config Configuration options
35383  */
35384 Roo.menu.ColorItem = function(config){
35385     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35386     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35387     this.palette = this.component;
35388     this.relayEvents(this.palette, ["select"]);
35389     if(this.selectHandler){
35390         this.on('select', this.selectHandler, this.scope);
35391     }
35392 };
35393 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35394  * Based on:
35395  * Ext JS Library 1.1.1
35396  * Copyright(c) 2006-2007, Ext JS, LLC.
35397  *
35398  * Originally Released Under LGPL - original licence link has changed is not relivant.
35399  *
35400  * Fork - LGPL
35401  * <script type="text/javascript">
35402  */
35403  
35404
35405 /**
35406  * @class Roo.menu.DateMenu
35407  * @extends Roo.menu.Menu
35408  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35409  * @constructor
35410  * Creates a new DateMenu
35411  * @param {Object} config Configuration options
35412  */
35413 Roo.menu.DateMenu = function(config){
35414     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35415     this.plain = true;
35416     var di = new Roo.menu.DateItem(config);
35417     this.add(di);
35418     /**
35419      * The {@link Roo.DatePicker} instance for this DateMenu
35420      * @type DatePicker
35421      */
35422     this.picker = di.picker;
35423     /**
35424      * @event select
35425      * @param {DatePicker} picker
35426      * @param {Date} date
35427      */
35428     this.relayEvents(di, ["select"]);
35429
35430     this.on('beforeshow', function(){
35431         if(this.picker){
35432             this.picker.hideMonthPicker(true);
35433         }
35434     }, this);
35435 };
35436 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35437     cls:'x-date-menu'
35438 });/*
35439  * Based on:
35440  * Ext JS Library 1.1.1
35441  * Copyright(c) 2006-2007, Ext JS, LLC.
35442  *
35443  * Originally Released Under LGPL - original licence link has changed is not relivant.
35444  *
35445  * Fork - LGPL
35446  * <script type="text/javascript">
35447  */
35448  
35449
35450 /**
35451  * @class Roo.menu.ColorMenu
35452  * @extends Roo.menu.Menu
35453  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35454  * @constructor
35455  * Creates a new ColorMenu
35456  * @param {Object} config Configuration options
35457  */
35458 Roo.menu.ColorMenu = function(config){
35459     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35460     this.plain = true;
35461     var ci = new Roo.menu.ColorItem(config);
35462     this.add(ci);
35463     /**
35464      * The {@link Roo.ColorPalette} instance for this ColorMenu
35465      * @type ColorPalette
35466      */
35467     this.palette = ci.palette;
35468     /**
35469      * @event select
35470      * @param {ColorPalette} palette
35471      * @param {String} color
35472      */
35473     this.relayEvents(ci, ["select"]);
35474 };
35475 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35476  * Based on:
35477  * Ext JS Library 1.1.1
35478  * Copyright(c) 2006-2007, Ext JS, LLC.
35479  *
35480  * Originally Released Under LGPL - original licence link has changed is not relivant.
35481  *
35482  * Fork - LGPL
35483  * <script type="text/javascript">
35484  */
35485  
35486 /**
35487  * @class Roo.form.Field
35488  * @extends Roo.BoxComponent
35489  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35490  * @constructor
35491  * Creates a new Field
35492  * @param {Object} config Configuration options
35493  */
35494 Roo.form.Field = function(config){
35495     Roo.form.Field.superclass.constructor.call(this, config);
35496 };
35497
35498 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35499     /**
35500      * @cfg {String} fieldLabel Label to use when rendering a form.
35501      */
35502        /**
35503      * @cfg {String} qtip Mouse over tip
35504      */
35505      
35506     /**
35507      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35508      */
35509     invalidClass : "x-form-invalid",
35510     /**
35511      * @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")
35512      */
35513     invalidText : "The value in this field is invalid",
35514     /**
35515      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35516      */
35517     focusClass : "x-form-focus",
35518     /**
35519      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35520       automatic validation (defaults to "keyup").
35521      */
35522     validationEvent : "keyup",
35523     /**
35524      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35525      */
35526     validateOnBlur : true,
35527     /**
35528      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35529      */
35530     validationDelay : 250,
35531     /**
35532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35533      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35534      */
35535     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35536     /**
35537      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35538      */
35539     fieldClass : "x-form-field",
35540     /**
35541      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35542      *<pre>
35543 Value         Description
35544 -----------   ----------------------------------------------------------------------
35545 qtip          Display a quick tip when the user hovers over the field
35546 title         Display a default browser title attribute popup
35547 under         Add a block div beneath the field containing the error text
35548 side          Add an error icon to the right of the field with a popup on hover
35549 [element id]  Add the error text directly to the innerHTML of the specified element
35550 </pre>
35551      */
35552     msgTarget : 'qtip',
35553     /**
35554      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35555      */
35556     msgFx : 'normal',
35557
35558     /**
35559      * @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.
35560      */
35561     readOnly : false,
35562
35563     /**
35564      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35565      */
35566     disabled : false,
35567
35568     /**
35569      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35570      */
35571     inputType : undefined,
35572     
35573     /**
35574      * @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).
35575          */
35576         tabIndex : undefined,
35577         
35578     // private
35579     isFormField : true,
35580
35581     // private
35582     hasFocus : false,
35583     /**
35584      * @property {Roo.Element} fieldEl
35585      * Element Containing the rendered Field (with label etc.)
35586      */
35587     /**
35588      * @cfg {Mixed} value A value to initialize this field with.
35589      */
35590     value : undefined,
35591
35592     /**
35593      * @cfg {String} name The field's HTML name attribute.
35594      */
35595     /**
35596      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35597      */
35598
35599         // private ??
35600         initComponent : function(){
35601         Roo.form.Field.superclass.initComponent.call(this);
35602         this.addEvents({
35603             /**
35604              * @event focus
35605              * Fires when this field receives input focus.
35606              * @param {Roo.form.Field} this
35607              */
35608             focus : true,
35609             /**
35610              * @event blur
35611              * Fires when this field loses input focus.
35612              * @param {Roo.form.Field} this
35613              */
35614             blur : true,
35615             /**
35616              * @event specialkey
35617              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35618              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35619              * @param {Roo.form.Field} this
35620              * @param {Roo.EventObject} e The event object
35621              */
35622             specialkey : true,
35623             /**
35624              * @event change
35625              * Fires just before the field blurs if the field value has changed.
35626              * @param {Roo.form.Field} this
35627              * @param {Mixed} newValue The new value
35628              * @param {Mixed} oldValue The original value
35629              */
35630             change : true,
35631             /**
35632              * @event invalid
35633              * Fires after the field has been marked as invalid.
35634              * @param {Roo.form.Field} this
35635              * @param {String} msg The validation message
35636              */
35637             invalid : true,
35638             /**
35639              * @event valid
35640              * Fires after the field has been validated with no errors.
35641              * @param {Roo.form.Field} this
35642              */
35643             valid : true,
35644              /**
35645              * @event keyup
35646              * Fires after the key up
35647              * @param {Roo.form.Field} this
35648              * @param {Roo.EventObject}  e The event Object
35649              */
35650             keyup : true
35651         });
35652     },
35653
35654     /**
35655      * Returns the name attribute of the field if available
35656      * @return {String} name The field name
35657      */
35658     getName: function(){
35659          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35660     },
35661
35662     // private
35663     onRender : function(ct, position){
35664         Roo.form.Field.superclass.onRender.call(this, ct, position);
35665         if(!this.el){
35666             var cfg = this.getAutoCreate();
35667             if(!cfg.name){
35668                 cfg.name = this.name || this.id;
35669             }
35670             if(this.inputType){
35671                 cfg.type = this.inputType;
35672             }
35673             this.el = ct.createChild(cfg, position);
35674         }
35675         var type = this.el.dom.type;
35676         if(type){
35677             if(type == 'password'){
35678                 type = 'text';
35679             }
35680             this.el.addClass('x-form-'+type);
35681         }
35682         if(this.readOnly){
35683             this.el.dom.readOnly = true;
35684         }
35685         if(this.tabIndex !== undefined){
35686             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35687         }
35688
35689         this.el.addClass([this.fieldClass, this.cls]);
35690         this.initValue();
35691     },
35692
35693     /**
35694      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35695      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35696      * @return {Roo.form.Field} this
35697      */
35698     applyTo : function(target){
35699         this.allowDomMove = false;
35700         this.el = Roo.get(target);
35701         this.render(this.el.dom.parentNode);
35702         return this;
35703     },
35704
35705     // private
35706     initValue : function(){
35707         if(this.value !== undefined){
35708             this.setValue(this.value);
35709         }else if(this.el.dom.value.length > 0){
35710             this.setValue(this.el.dom.value);
35711         }
35712     },
35713
35714     /**
35715      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35716      */
35717     isDirty : function() {
35718         if(this.disabled) {
35719             return false;
35720         }
35721         return String(this.getValue()) !== String(this.originalValue);
35722     },
35723
35724     // private
35725     afterRender : function(){
35726         Roo.form.Field.superclass.afterRender.call(this);
35727         this.initEvents();
35728     },
35729
35730     // private
35731     fireKey : function(e){
35732         //Roo.log('field ' + e.getKey());
35733         if(e.isNavKeyPress()){
35734             this.fireEvent("specialkey", this, e);
35735         }
35736     },
35737
35738     /**
35739      * Resets the current field value to the originally loaded value and clears any validation messages
35740      */
35741     reset : function(){
35742         this.setValue(this.originalValue);
35743         this.clearInvalid();
35744     },
35745
35746     // private
35747     initEvents : function(){
35748         // safari killled keypress - so keydown is now used..
35749         this.el.on("keydown" , this.fireKey,  this);
35750         this.el.on("focus", this.onFocus,  this);
35751         this.el.on("blur", this.onBlur,  this);
35752         this.el.relayEvent('keyup', this);
35753
35754         // reference to original value for reset
35755         this.originalValue = this.getValue();
35756     },
35757
35758     // private
35759     onFocus : function(){
35760         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35761             this.el.addClass(this.focusClass);
35762         }
35763         if(!this.hasFocus){
35764             this.hasFocus = true;
35765             this.startValue = this.getValue();
35766             this.fireEvent("focus", this);
35767         }
35768     },
35769
35770     beforeBlur : Roo.emptyFn,
35771
35772     // private
35773     onBlur : function(){
35774         this.beforeBlur();
35775         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35776             this.el.removeClass(this.focusClass);
35777         }
35778         this.hasFocus = false;
35779         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35780             this.validate();
35781         }
35782         var v = this.getValue();
35783         if(String(v) !== String(this.startValue)){
35784             this.fireEvent('change', this, v, this.startValue);
35785         }
35786         this.fireEvent("blur", this);
35787     },
35788
35789     /**
35790      * Returns whether or not the field value is currently valid
35791      * @param {Boolean} preventMark True to disable marking the field invalid
35792      * @return {Boolean} True if the value is valid, else false
35793      */
35794     isValid : function(preventMark){
35795         if(this.disabled){
35796             return true;
35797         }
35798         var restore = this.preventMark;
35799         this.preventMark = preventMark === true;
35800         var v = this.validateValue(this.processValue(this.getRawValue()));
35801         this.preventMark = restore;
35802         return v;
35803     },
35804
35805     /**
35806      * Validates the field value
35807      * @return {Boolean} True if the value is valid, else false
35808      */
35809     validate : function(){
35810         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35811             this.clearInvalid();
35812             return true;
35813         }
35814         return false;
35815     },
35816
35817     processValue : function(value){
35818         return value;
35819     },
35820
35821     // private
35822     // Subclasses should provide the validation implementation by overriding this
35823     validateValue : function(value){
35824         return true;
35825     },
35826
35827     /**
35828      * Mark this field as invalid
35829      * @param {String} msg The validation message
35830      */
35831     markInvalid : function(msg){
35832         if(!this.rendered || this.preventMark){ // not rendered
35833             return;
35834         }
35835         this.el.addClass(this.invalidClass);
35836         msg = msg || this.invalidText;
35837         switch(this.msgTarget){
35838             case 'qtip':
35839                 this.el.dom.qtip = msg;
35840                 this.el.dom.qclass = 'x-form-invalid-tip';
35841                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35842                     Roo.QuickTips.enable();
35843                 }
35844                 break;
35845             case 'title':
35846                 this.el.dom.title = msg;
35847                 break;
35848             case 'under':
35849                 if(!this.errorEl){
35850                     var elp = this.el.findParent('.x-form-element', 5, true);
35851                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35852                     this.errorEl.setWidth(elp.getWidth(true)-20);
35853                 }
35854                 this.errorEl.update(msg);
35855                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35856                 break;
35857             case 'side':
35858                 if(!this.errorIcon){
35859                     var elp = this.el.findParent('.x-form-element', 5, true);
35860                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35861                 }
35862                 this.alignErrorIcon();
35863                 this.errorIcon.dom.qtip = msg;
35864                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35865                 this.errorIcon.show();
35866                 this.on('resize', this.alignErrorIcon, this);
35867                 break;
35868             default:
35869                 var t = Roo.getDom(this.msgTarget);
35870                 t.innerHTML = msg;
35871                 t.style.display = this.msgDisplay;
35872                 break;
35873         }
35874         this.fireEvent('invalid', this, msg);
35875     },
35876
35877     // private
35878     alignErrorIcon : function(){
35879         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35880     },
35881
35882     /**
35883      * Clear any invalid styles/messages for this field
35884      */
35885     clearInvalid : function(){
35886         if(!this.rendered || this.preventMark){ // not rendered
35887             return;
35888         }
35889         this.el.removeClass(this.invalidClass);
35890         switch(this.msgTarget){
35891             case 'qtip':
35892                 this.el.dom.qtip = '';
35893                 break;
35894             case 'title':
35895                 this.el.dom.title = '';
35896                 break;
35897             case 'under':
35898                 if(this.errorEl){
35899                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35900                 }
35901                 break;
35902             case 'side':
35903                 if(this.errorIcon){
35904                     this.errorIcon.dom.qtip = '';
35905                     this.errorIcon.hide();
35906                     this.un('resize', this.alignErrorIcon, this);
35907                 }
35908                 break;
35909             default:
35910                 var t = Roo.getDom(this.msgTarget);
35911                 t.innerHTML = '';
35912                 t.style.display = 'none';
35913                 break;
35914         }
35915         this.fireEvent('valid', this);
35916     },
35917
35918     /**
35919      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35920      * @return {Mixed} value The field value
35921      */
35922     getRawValue : function(){
35923         var v = this.el.getValue();
35924         if(v === this.emptyText){
35925             v = '';
35926         }
35927         return v;
35928     },
35929
35930     /**
35931      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35932      * @return {Mixed} value The field value
35933      */
35934     getValue : function(){
35935         var v = this.el.getValue();
35936         if(v === this.emptyText || v === undefined){
35937             v = '';
35938         }
35939         return v;
35940     },
35941
35942     /**
35943      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35944      * @param {Mixed} value The value to set
35945      */
35946     setRawValue : function(v){
35947         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35948     },
35949
35950     /**
35951      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35952      * @param {Mixed} value The value to set
35953      */
35954     setValue : function(v){
35955         this.value = v;
35956         if(this.rendered){
35957             this.el.dom.value = (v === null || v === undefined ? '' : v);
35958              this.validate();
35959         }
35960     },
35961
35962     adjustSize : function(w, h){
35963         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35964         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35965         return s;
35966     },
35967
35968     adjustWidth : function(tag, w){
35969         tag = tag.toLowerCase();
35970         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35971             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35972                 if(tag == 'input'){
35973                     return w + 2;
35974                 }
35975                 if(tag = 'textarea'){
35976                     return w-2;
35977                 }
35978             }else if(Roo.isOpera){
35979                 if(tag == 'input'){
35980                     return w + 2;
35981                 }
35982                 if(tag = 'textarea'){
35983                     return w-2;
35984                 }
35985             }
35986         }
35987         return w;
35988     }
35989 });
35990
35991
35992 // anything other than normal should be considered experimental
35993 Roo.form.Field.msgFx = {
35994     normal : {
35995         show: function(msgEl, f){
35996             msgEl.setDisplayed('block');
35997         },
35998
35999         hide : function(msgEl, f){
36000             msgEl.setDisplayed(false).update('');
36001         }
36002     },
36003
36004     slide : {
36005         show: function(msgEl, f){
36006             msgEl.slideIn('t', {stopFx:true});
36007         },
36008
36009         hide : function(msgEl, f){
36010             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
36011         }
36012     },
36013
36014     slideRight : {
36015         show: function(msgEl, f){
36016             msgEl.fixDisplay();
36017             msgEl.alignTo(f.el, 'tl-tr');
36018             msgEl.slideIn('l', {stopFx:true});
36019         },
36020
36021         hide : function(msgEl, f){
36022             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
36023         }
36024     }
36025 };/*
36026  * Based on:
36027  * Ext JS Library 1.1.1
36028  * Copyright(c) 2006-2007, Ext JS, LLC.
36029  *
36030  * Originally Released Under LGPL - original licence link has changed is not relivant.
36031  *
36032  * Fork - LGPL
36033  * <script type="text/javascript">
36034  */
36035  
36036
36037 /**
36038  * @class Roo.form.TextField
36039  * @extends Roo.form.Field
36040  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
36041  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
36042  * @constructor
36043  * Creates a new TextField
36044  * @param {Object} config Configuration options
36045  */
36046 Roo.form.TextField = function(config){
36047     Roo.form.TextField.superclass.constructor.call(this, config);
36048     this.addEvents({
36049         /**
36050          * @event autosize
36051          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
36052          * according to the default logic, but this event provides a hook for the developer to apply additional
36053          * logic at runtime to resize the field if needed.
36054              * @param {Roo.form.Field} this This text field
36055              * @param {Number} width The new field width
36056              */
36057         autosize : true
36058     });
36059 };
36060
36061 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
36062     /**
36063      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
36064      */
36065     grow : false,
36066     /**
36067      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
36068      */
36069     growMin : 30,
36070     /**
36071      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
36072      */
36073     growMax : 800,
36074     /**
36075      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
36076      */
36077     vtype : null,
36078     /**
36079      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
36080      */
36081     maskRe : null,
36082     /**
36083      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
36084      */
36085     disableKeyFilter : false,
36086     /**
36087      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
36088      */
36089     allowBlank : true,
36090     /**
36091      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
36092      */
36093     minLength : 0,
36094     /**
36095      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
36096      */
36097     maxLength : Number.MAX_VALUE,
36098     /**
36099      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
36100      */
36101     minLengthText : "The minimum length for this field is {0}",
36102     /**
36103      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
36104      */
36105     maxLengthText : "The maximum length for this field is {0}",
36106     /**
36107      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
36108      */
36109     selectOnFocus : false,
36110     /**
36111      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
36112      */
36113     blankText : "This field is required",
36114     /**
36115      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
36116      * If available, this function will be called only after the basic validators all return true, and will be passed the
36117      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
36118      */
36119     validator : null,
36120     /**
36121      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
36122      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
36123      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
36124      */
36125     regex : null,
36126     /**
36127      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
36128      */
36129     regexText : "",
36130     /**
36131      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
36132      */
36133     emptyText : null,
36134     /**
36135      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
36136      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
36137      */
36138     emptyClass : 'x-form-empty-field',
36139
36140     // private
36141     initEvents : function(){
36142         Roo.form.TextField.superclass.initEvents.call(this);
36143         if(this.validationEvent == 'keyup'){
36144             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
36145             this.el.on('keyup', this.filterValidation, this);
36146         }
36147         else if(this.validationEvent !== false){
36148             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
36149         }
36150         if(this.selectOnFocus || this.emptyText){
36151             this.on("focus", this.preFocus, this);
36152             if(this.emptyText){
36153                 this.on('blur', this.postBlur, this);
36154                 this.applyEmptyText();
36155             }
36156         }
36157         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
36158             this.el.on("keypress", this.filterKeys, this);
36159         }
36160         if(this.grow){
36161             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
36162             this.el.on("click", this.autoSize,  this);
36163         }
36164     },
36165
36166     processValue : function(value){
36167         if(this.stripCharsRe){
36168             var newValue = value.replace(this.stripCharsRe, '');
36169             if(newValue !== value){
36170                 this.setRawValue(newValue);
36171                 return newValue;
36172             }
36173         }
36174         return value;
36175     },
36176
36177     filterValidation : function(e){
36178         if(!e.isNavKeyPress()){
36179             this.validationTask.delay(this.validationDelay);
36180         }
36181     },
36182
36183     // private
36184     onKeyUp : function(e){
36185         if(!e.isNavKeyPress()){
36186             this.autoSize();
36187         }
36188     },
36189
36190     /**
36191      * Resets the current field value to the originally-loaded value and clears any validation messages.
36192      * Also adds emptyText and emptyClass if the original value was blank.
36193      */
36194     reset : function(){
36195         Roo.form.TextField.superclass.reset.call(this);
36196         this.applyEmptyText();
36197     },
36198
36199     applyEmptyText : function(){
36200         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
36201             this.setRawValue(this.emptyText);
36202             this.el.addClass(this.emptyClass);
36203         }
36204     },
36205
36206     // private
36207     preFocus : function(){
36208         if(this.emptyText){
36209             if(this.el.dom.value == this.emptyText){
36210                 this.setRawValue('');
36211             }
36212             this.el.removeClass(this.emptyClass);
36213         }
36214         if(this.selectOnFocus){
36215             this.el.dom.select();
36216         }
36217     },
36218
36219     // private
36220     postBlur : function(){
36221         this.applyEmptyText();
36222     },
36223
36224     // private
36225     filterKeys : function(e){
36226         var k = e.getKey();
36227         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
36228             return;
36229         }
36230         var c = e.getCharCode(), cc = String.fromCharCode(c);
36231         if(Roo.isIE && (e.isSpecialKey() || !cc)){
36232             return;
36233         }
36234         if(!this.maskRe.test(cc)){
36235             e.stopEvent();
36236         }
36237     },
36238
36239     setValue : function(v){
36240         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
36241             this.el.removeClass(this.emptyClass);
36242         }
36243         Roo.form.TextField.superclass.setValue.apply(this, arguments);
36244         this.applyEmptyText();
36245         this.autoSize();
36246     },
36247
36248     /**
36249      * Validates a value according to the field's validation rules and marks the field as invalid
36250      * if the validation fails
36251      * @param {Mixed} value The value to validate
36252      * @return {Boolean} True if the value is valid, else false
36253      */
36254     validateValue : function(value){
36255         if(value.length < 1 || value === this.emptyText){ // if it's blank
36256              if(this.allowBlank){
36257                 this.clearInvalid();
36258                 return true;
36259              }else{
36260                 this.markInvalid(this.blankText);
36261                 return false;
36262              }
36263         }
36264         if(value.length < this.minLength){
36265             this.markInvalid(String.format(this.minLengthText, this.minLength));
36266             return false;
36267         }
36268         if(value.length > this.maxLength){
36269             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
36270             return false;
36271         }
36272         if(this.vtype){
36273             var vt = Roo.form.VTypes;
36274             if(!vt[this.vtype](value, this)){
36275                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
36276                 return false;
36277             }
36278         }
36279         if(typeof this.validator == "function"){
36280             var msg = this.validator(value);
36281             if(msg !== true){
36282                 this.markInvalid(msg);
36283                 return false;
36284             }
36285         }
36286         if(this.regex && !this.regex.test(value)){
36287             this.markInvalid(this.regexText);
36288             return false;
36289         }
36290         return true;
36291     },
36292
36293     /**
36294      * Selects text in this field
36295      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
36296      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
36297      */
36298     selectText : function(start, end){
36299         var v = this.getRawValue();
36300         if(v.length > 0){
36301             start = start === undefined ? 0 : start;
36302             end = end === undefined ? v.length : end;
36303             var d = this.el.dom;
36304             if(d.setSelectionRange){
36305                 d.setSelectionRange(start, end);
36306             }else if(d.createTextRange){
36307                 var range = d.createTextRange();
36308                 range.moveStart("character", start);
36309                 range.moveEnd("character", v.length-end);
36310                 range.select();
36311             }
36312         }
36313     },
36314
36315     /**
36316      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36317      * This only takes effect if grow = true, and fires the autosize event.
36318      */
36319     autoSize : function(){
36320         if(!this.grow || !this.rendered){
36321             return;
36322         }
36323         if(!this.metrics){
36324             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36325         }
36326         var el = this.el;
36327         var v = el.dom.value;
36328         var d = document.createElement('div');
36329         d.appendChild(document.createTextNode(v));
36330         v = d.innerHTML;
36331         d = null;
36332         v += "&#160;";
36333         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36334         this.el.setWidth(w);
36335         this.fireEvent("autosize", this, w);
36336     }
36337 });/*
36338  * Based on:
36339  * Ext JS Library 1.1.1
36340  * Copyright(c) 2006-2007, Ext JS, LLC.
36341  *
36342  * Originally Released Under LGPL - original licence link has changed is not relivant.
36343  *
36344  * Fork - LGPL
36345  * <script type="text/javascript">
36346  */
36347  
36348 /**
36349  * @class Roo.form.Hidden
36350  * @extends Roo.form.TextField
36351  * Simple Hidden element used on forms 
36352  * 
36353  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36354  * 
36355  * @constructor
36356  * Creates a new Hidden form element.
36357  * @param {Object} config Configuration options
36358  */
36359
36360
36361
36362 // easy hidden field...
36363 Roo.form.Hidden = function(config){
36364     Roo.form.Hidden.superclass.constructor.call(this, config);
36365 };
36366   
36367 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36368     fieldLabel:      '',
36369     inputType:      'hidden',
36370     width:          50,
36371     allowBlank:     true,
36372     labelSeparator: '',
36373     hidden:         true,
36374     itemCls :       'x-form-item-display-none'
36375
36376
36377 });
36378
36379
36380 /*
36381  * Based on:
36382  * Ext JS Library 1.1.1
36383  * Copyright(c) 2006-2007, Ext JS, LLC.
36384  *
36385  * Originally Released Under LGPL - original licence link has changed is not relivant.
36386  *
36387  * Fork - LGPL
36388  * <script type="text/javascript">
36389  */
36390  
36391 /**
36392  * @class Roo.form.TriggerField
36393  * @extends Roo.form.TextField
36394  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36395  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36396  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36397  * for which you can provide a custom implementation.  For example:
36398  * <pre><code>
36399 var trigger = new Roo.form.TriggerField();
36400 trigger.onTriggerClick = myTriggerFn;
36401 trigger.applyTo('my-field');
36402 </code></pre>
36403  *
36404  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36405  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36406  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36407  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36408  * @constructor
36409  * Create a new TriggerField.
36410  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36411  * to the base TextField)
36412  */
36413 Roo.form.TriggerField = function(config){
36414     this.mimicing = false;
36415     Roo.form.TriggerField.superclass.constructor.call(this, config);
36416 };
36417
36418 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36419     /**
36420      * @cfg {String} triggerClass A CSS class to apply to the trigger
36421      */
36422     /**
36423      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36424      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36425      */
36426     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36427     /**
36428      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36429      */
36430     hideTrigger:false,
36431
36432     /** @cfg {Boolean} grow @hide */
36433     /** @cfg {Number} growMin @hide */
36434     /** @cfg {Number} growMax @hide */
36435
36436     /**
36437      * @hide 
36438      * @method
36439      */
36440     autoSize: Roo.emptyFn,
36441     // private
36442     monitorTab : true,
36443     // private
36444     deferHeight : true,
36445
36446     
36447     actionMode : 'wrap',
36448     // private
36449     onResize : function(w, h){
36450         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36451         if(typeof w == 'number'){
36452             var x = w - this.trigger.getWidth();
36453             this.el.setWidth(this.adjustWidth('input', x));
36454             this.trigger.setStyle('left', x+'px');
36455         }
36456     },
36457
36458     // private
36459     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36460
36461     // private
36462     getResizeEl : function(){
36463         return this.wrap;
36464     },
36465
36466     // private
36467     getPositionEl : function(){
36468         return this.wrap;
36469     },
36470
36471     // private
36472     alignErrorIcon : function(){
36473         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36474     },
36475
36476     // private
36477     onRender : function(ct, position){
36478         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36479         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36480         this.trigger = this.wrap.createChild(this.triggerConfig ||
36481                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36482         if(this.hideTrigger){
36483             this.trigger.setDisplayed(false);
36484         }
36485         this.initTrigger();
36486         if(!this.width){
36487             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36488         }
36489     },
36490
36491     // private
36492     initTrigger : function(){
36493         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36494         this.trigger.addClassOnOver('x-form-trigger-over');
36495         this.trigger.addClassOnClick('x-form-trigger-click');
36496     },
36497
36498     // private
36499     onDestroy : function(){
36500         if(this.trigger){
36501             this.trigger.removeAllListeners();
36502             this.trigger.remove();
36503         }
36504         if(this.wrap){
36505             this.wrap.remove();
36506         }
36507         Roo.form.TriggerField.superclass.onDestroy.call(this);
36508     },
36509
36510     // private
36511     onFocus : function(){
36512         Roo.form.TriggerField.superclass.onFocus.call(this);
36513         if(!this.mimicing){
36514             this.wrap.addClass('x-trigger-wrap-focus');
36515             this.mimicing = true;
36516             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36517             if(this.monitorTab){
36518                 this.el.on("keydown", this.checkTab, this);
36519             }
36520         }
36521     },
36522
36523     // private
36524     checkTab : function(e){
36525         if(e.getKey() == e.TAB){
36526             this.triggerBlur();
36527         }
36528     },
36529
36530     // private
36531     onBlur : function(){
36532         // do nothing
36533     },
36534
36535     // private
36536     mimicBlur : function(e, t){
36537         if(!this.wrap.contains(t) && this.validateBlur()){
36538             this.triggerBlur();
36539         }
36540     },
36541
36542     // private
36543     triggerBlur : function(){
36544         this.mimicing = false;
36545         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36546         if(this.monitorTab){
36547             this.el.un("keydown", this.checkTab, this);
36548         }
36549         this.wrap.removeClass('x-trigger-wrap-focus');
36550         Roo.form.TriggerField.superclass.onBlur.call(this);
36551     },
36552
36553     // private
36554     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36555     validateBlur : function(e, t){
36556         return true;
36557     },
36558
36559     // private
36560     onDisable : function(){
36561         Roo.form.TriggerField.superclass.onDisable.call(this);
36562         if(this.wrap){
36563             this.wrap.addClass('x-item-disabled');
36564         }
36565     },
36566
36567     // private
36568     onEnable : function(){
36569         Roo.form.TriggerField.superclass.onEnable.call(this);
36570         if(this.wrap){
36571             this.wrap.removeClass('x-item-disabled');
36572         }
36573     },
36574
36575     // private
36576     onShow : function(){
36577         var ae = this.getActionEl();
36578         
36579         if(ae){
36580             ae.dom.style.display = '';
36581             ae.dom.style.visibility = 'visible';
36582         }
36583     },
36584
36585     // private
36586     
36587     onHide : function(){
36588         var ae = this.getActionEl();
36589         ae.dom.style.display = 'none';
36590     },
36591
36592     /**
36593      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36594      * by an implementing function.
36595      * @method
36596      * @param {EventObject} e
36597      */
36598     onTriggerClick : Roo.emptyFn
36599 });
36600
36601 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36602 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36603 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36604 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36605     initComponent : function(){
36606         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36607
36608         this.triggerConfig = {
36609             tag:'span', cls:'x-form-twin-triggers', cn:[
36610             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36611             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36612         ]};
36613     },
36614
36615     getTrigger : function(index){
36616         return this.triggers[index];
36617     },
36618
36619     initTrigger : function(){
36620         var ts = this.trigger.select('.x-form-trigger', true);
36621         this.wrap.setStyle('overflow', 'hidden');
36622         var triggerField = this;
36623         ts.each(function(t, all, index){
36624             t.hide = function(){
36625                 var w = triggerField.wrap.getWidth();
36626                 this.dom.style.display = 'none';
36627                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36628             };
36629             t.show = function(){
36630                 var w = triggerField.wrap.getWidth();
36631                 this.dom.style.display = '';
36632                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36633             };
36634             var triggerIndex = 'Trigger'+(index+1);
36635
36636             if(this['hide'+triggerIndex]){
36637                 t.dom.style.display = 'none';
36638             }
36639             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36640             t.addClassOnOver('x-form-trigger-over');
36641             t.addClassOnClick('x-form-trigger-click');
36642         }, this);
36643         this.triggers = ts.elements;
36644     },
36645
36646     onTrigger1Click : Roo.emptyFn,
36647     onTrigger2Click : Roo.emptyFn
36648 });/*
36649  * Based on:
36650  * Ext JS Library 1.1.1
36651  * Copyright(c) 2006-2007, Ext JS, LLC.
36652  *
36653  * Originally Released Under LGPL - original licence link has changed is not relivant.
36654  *
36655  * Fork - LGPL
36656  * <script type="text/javascript">
36657  */
36658  
36659 /**
36660  * @class Roo.form.TextArea
36661  * @extends Roo.form.TextField
36662  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36663  * support for auto-sizing.
36664  * @constructor
36665  * Creates a new TextArea
36666  * @param {Object} config Configuration options
36667  */
36668 Roo.form.TextArea = function(config){
36669     Roo.form.TextArea.superclass.constructor.call(this, config);
36670     // these are provided exchanges for backwards compat
36671     // minHeight/maxHeight were replaced by growMin/growMax to be
36672     // compatible with TextField growing config values
36673     if(this.minHeight !== undefined){
36674         this.growMin = this.minHeight;
36675     }
36676     if(this.maxHeight !== undefined){
36677         this.growMax = this.maxHeight;
36678     }
36679 };
36680
36681 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36682     /**
36683      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36684      */
36685     growMin : 60,
36686     /**
36687      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36688      */
36689     growMax: 1000,
36690     /**
36691      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36692      * in the field (equivalent to setting overflow: hidden, defaults to false)
36693      */
36694     preventScrollbars: false,
36695     /**
36696      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36697      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36698      */
36699
36700     // private
36701     onRender : function(ct, position){
36702         if(!this.el){
36703             this.defaultAutoCreate = {
36704                 tag: "textarea",
36705                 style:"width:300px;height:60px;",
36706                 autocomplete: "off"
36707             };
36708         }
36709         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36710         if(this.grow){
36711             this.textSizeEl = Roo.DomHelper.append(document.body, {
36712                 tag: "pre", cls: "x-form-grow-sizer"
36713             });
36714             if(this.preventScrollbars){
36715                 this.el.setStyle("overflow", "hidden");
36716             }
36717             this.el.setHeight(this.growMin);
36718         }
36719     },
36720
36721     onDestroy : function(){
36722         if(this.textSizeEl){
36723             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36724         }
36725         Roo.form.TextArea.superclass.onDestroy.call(this);
36726     },
36727
36728     // private
36729     onKeyUp : function(e){
36730         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36731             this.autoSize();
36732         }
36733     },
36734
36735     /**
36736      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36737      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36738      */
36739     autoSize : function(){
36740         if(!this.grow || !this.textSizeEl){
36741             return;
36742         }
36743         var el = this.el;
36744         var v = el.dom.value;
36745         var ts = this.textSizeEl;
36746
36747         ts.innerHTML = '';
36748         ts.appendChild(document.createTextNode(v));
36749         v = ts.innerHTML;
36750
36751         Roo.fly(ts).setWidth(this.el.getWidth());
36752         if(v.length < 1){
36753             v = "&#160;&#160;";
36754         }else{
36755             if(Roo.isIE){
36756                 v = v.replace(/\n/g, '<p>&#160;</p>');
36757             }
36758             v += "&#160;\n&#160;";
36759         }
36760         ts.innerHTML = v;
36761         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36762         if(h != this.lastHeight){
36763             this.lastHeight = h;
36764             this.el.setHeight(h);
36765             this.fireEvent("autosize", this, h);
36766         }
36767     }
36768 });/*
36769  * Based on:
36770  * Ext JS Library 1.1.1
36771  * Copyright(c) 2006-2007, Ext JS, LLC.
36772  *
36773  * Originally Released Under LGPL - original licence link has changed is not relivant.
36774  *
36775  * Fork - LGPL
36776  * <script type="text/javascript">
36777  */
36778  
36779
36780 /**
36781  * @class Roo.form.NumberField
36782  * @extends Roo.form.TextField
36783  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36784  * @constructor
36785  * Creates a new NumberField
36786  * @param {Object} config Configuration options
36787  */
36788 Roo.form.NumberField = function(config){
36789     Roo.form.NumberField.superclass.constructor.call(this, config);
36790 };
36791
36792 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36793     /**
36794      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36795      */
36796     fieldClass: "x-form-field x-form-num-field",
36797     /**
36798      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36799      */
36800     allowDecimals : true,
36801     /**
36802      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36803      */
36804     decimalSeparator : ".",
36805     /**
36806      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36807      */
36808     decimalPrecision : 2,
36809     /**
36810      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36811      */
36812     allowNegative : true,
36813     /**
36814      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36815      */
36816     minValue : Number.NEGATIVE_INFINITY,
36817     /**
36818      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36819      */
36820     maxValue : Number.MAX_VALUE,
36821     /**
36822      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36823      */
36824     minText : "The minimum value for this field is {0}",
36825     /**
36826      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36827      */
36828     maxText : "The maximum value for this field is {0}",
36829     /**
36830      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36831      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36832      */
36833     nanText : "{0} is not a valid number",
36834
36835     // private
36836     initEvents : function(){
36837         Roo.form.NumberField.superclass.initEvents.call(this);
36838         var allowed = "0123456789";
36839         if(this.allowDecimals){
36840             allowed += this.decimalSeparator;
36841         }
36842         if(this.allowNegative){
36843             allowed += "-";
36844         }
36845         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36846         var keyPress = function(e){
36847             var k = e.getKey();
36848             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36849                 return;
36850             }
36851             var c = e.getCharCode();
36852             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36853                 e.stopEvent();
36854             }
36855         };
36856         this.el.on("keypress", keyPress, this);
36857     },
36858
36859     // private
36860     validateValue : function(value){
36861         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36862             return false;
36863         }
36864         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36865              return true;
36866         }
36867         var num = this.parseValue(value);
36868         if(isNaN(num)){
36869             this.markInvalid(String.format(this.nanText, value));
36870             return false;
36871         }
36872         if(num < this.minValue){
36873             this.markInvalid(String.format(this.minText, this.minValue));
36874             return false;
36875         }
36876         if(num > this.maxValue){
36877             this.markInvalid(String.format(this.maxText, this.maxValue));
36878             return false;
36879         }
36880         return true;
36881     },
36882
36883     getValue : function(){
36884         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36885     },
36886
36887     // private
36888     parseValue : function(value){
36889         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36890         return isNaN(value) ? '' : value;
36891     },
36892
36893     // private
36894     fixPrecision : function(value){
36895         var nan = isNaN(value);
36896         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36897             return nan ? '' : value;
36898         }
36899         return parseFloat(value).toFixed(this.decimalPrecision);
36900     },
36901
36902     setValue : function(v){
36903         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36904     },
36905
36906     // private
36907     decimalPrecisionFcn : function(v){
36908         return Math.floor(v);
36909     },
36910
36911     beforeBlur : function(){
36912         var v = this.parseValue(this.getRawValue());
36913         if(v){
36914             this.setValue(this.fixPrecision(v));
36915         }
36916     }
36917 });/*
36918  * Based on:
36919  * Ext JS Library 1.1.1
36920  * Copyright(c) 2006-2007, Ext JS, LLC.
36921  *
36922  * Originally Released Under LGPL - original licence link has changed is not relivant.
36923  *
36924  * Fork - LGPL
36925  * <script type="text/javascript">
36926  */
36927  
36928 /**
36929  * @class Roo.form.DateField
36930  * @extends Roo.form.TriggerField
36931  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36932 * @constructor
36933 * Create a new DateField
36934 * @param {Object} config
36935  */
36936 Roo.form.DateField = function(config){
36937     Roo.form.DateField.superclass.constructor.call(this, config);
36938     
36939       this.addEvents({
36940          
36941         /**
36942          * @event select
36943          * Fires when a date is selected
36944              * @param {Roo.form.DateField} combo This combo box
36945              * @param {Date} date The date selected
36946              */
36947         'select' : true
36948          
36949     });
36950     
36951     
36952     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36953     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36954     this.ddMatch = null;
36955     if(this.disabledDates){
36956         var dd = this.disabledDates;
36957         var re = "(?:";
36958         for(var i = 0; i < dd.length; i++){
36959             re += dd[i];
36960             if(i != dd.length-1) re += "|";
36961         }
36962         this.ddMatch = new RegExp(re + ")");
36963     }
36964 };
36965
36966 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36967     /**
36968      * @cfg {String} format
36969      * The default date format string which can be overriden for localization support.  The format must be
36970      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36971      */
36972     format : "m/d/y",
36973     /**
36974      * @cfg {String} altFormats
36975      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36976      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36977      */
36978     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36979     /**
36980      * @cfg {Array} disabledDays
36981      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36982      */
36983     disabledDays : null,
36984     /**
36985      * @cfg {String} disabledDaysText
36986      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36987      */
36988     disabledDaysText : "Disabled",
36989     /**
36990      * @cfg {Array} disabledDates
36991      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36992      * expression so they are very powerful. Some examples:
36993      * <ul>
36994      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36995      * <li>["03/08", "09/16"] would disable those days for every year</li>
36996      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36997      * <li>["03/../2006"] would disable every day in March 2006</li>
36998      * <li>["^03"] would disable every day in every March</li>
36999      * </ul>
37000      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
37001      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
37002      */
37003     disabledDates : null,
37004     /**
37005      * @cfg {String} disabledDatesText
37006      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
37007      */
37008     disabledDatesText : "Disabled",
37009     /**
37010      * @cfg {Date/String} minValue
37011      * The minimum allowed date. Can be either a Javascript date object or a string date in a
37012      * valid format (defaults to null).
37013      */
37014     minValue : null,
37015     /**
37016      * @cfg {Date/String} maxValue
37017      * The maximum allowed date. Can be either a Javascript date object or a string date in a
37018      * valid format (defaults to null).
37019      */
37020     maxValue : null,
37021     /**
37022      * @cfg {String} minText
37023      * The error text to display when the date in the cell is before minValue (defaults to
37024      * 'The date in this field must be after {minValue}').
37025      */
37026     minText : "The date in this field must be equal to or after {0}",
37027     /**
37028      * @cfg {String} maxText
37029      * The error text to display when the date in the cell is after maxValue (defaults to
37030      * 'The date in this field must be before {maxValue}').
37031      */
37032     maxText : "The date in this field must be equal to or before {0}",
37033     /**
37034      * @cfg {String} invalidText
37035      * The error text to display when the date in the field is invalid (defaults to
37036      * '{value} is not a valid date - it must be in the format {format}').
37037      */
37038     invalidText : "{0} is not a valid date - it must be in the format {1}",
37039     /**
37040      * @cfg {String} triggerClass
37041      * An additional CSS class used to style the trigger button.  The trigger will always get the
37042      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
37043      * which displays a calendar icon).
37044      */
37045     triggerClass : 'x-form-date-trigger',
37046     
37047
37048     /**
37049      * @cfg {bool} useIso
37050      * if enabled, then the date field will use a hidden field to store the 
37051      * real value as iso formated date. default (false)
37052      */ 
37053     useIso : false,
37054     /**
37055      * @cfg {String/Object} autoCreate
37056      * A DomHelper element spec, or true for a default element spec (defaults to
37057      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
37058      */ 
37059     // private
37060     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
37061     
37062     // private
37063     hiddenField: false,
37064     
37065     onRender : function(ct, position)
37066     {
37067         Roo.form.DateField.superclass.onRender.call(this, ct, position);
37068         if (this.useIso) {
37069             this.el.dom.removeAttribute('name'); 
37070             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
37071                     'before', true);
37072             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
37073             // prevent input submission
37074             this.hiddenName = this.name;
37075         }
37076             
37077             
37078     },
37079     
37080     // private
37081     validateValue : function(value)
37082     {
37083         value = this.formatDate(value);
37084         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
37085             return false;
37086         }
37087         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
37088              return true;
37089         }
37090         var svalue = value;
37091         value = this.parseDate(value);
37092         if(!value){
37093             this.markInvalid(String.format(this.invalidText, svalue, this.format));
37094             return false;
37095         }
37096         var time = value.getTime();
37097         if(this.minValue && time < this.minValue.getTime()){
37098             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
37099             return false;
37100         }
37101         if(this.maxValue && time > this.maxValue.getTime()){
37102             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
37103             return false;
37104         }
37105         if(this.disabledDays){
37106             var day = value.getDay();
37107             for(var i = 0; i < this.disabledDays.length; i++) {
37108                 if(day === this.disabledDays[i]){
37109                     this.markInvalid(this.disabledDaysText);
37110                     return false;
37111                 }
37112             }
37113         }
37114         var fvalue = this.formatDate(value);
37115         if(this.ddMatch && this.ddMatch.test(fvalue)){
37116             this.markInvalid(String.format(this.disabledDatesText, fvalue));
37117             return false;
37118         }
37119         return true;
37120     },
37121
37122     // private
37123     // Provides logic to override the default TriggerField.validateBlur which just returns true
37124     validateBlur : function(){
37125         return !this.menu || !this.menu.isVisible();
37126     },
37127
37128     /**
37129      * Returns the current date value of the date field.
37130      * @return {Date} The date value
37131      */
37132     getValue : function(){
37133         
37134         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
37135     },
37136
37137     /**
37138      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
37139      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
37140      * (the default format used is "m/d/y").
37141      * <br />Usage:
37142      * <pre><code>
37143 //All of these calls set the same date value (May 4, 2006)
37144
37145 //Pass a date object:
37146 var dt = new Date('5/4/06');
37147 dateField.setValue(dt);
37148
37149 //Pass a date string (default format):
37150 dateField.setValue('5/4/06');
37151
37152 //Pass a date string (custom format):
37153 dateField.format = 'Y-m-d';
37154 dateField.setValue('2006-5-4');
37155 </code></pre>
37156      * @param {String/Date} date The date or valid date string
37157      */
37158     setValue : function(date){
37159         if (this.hiddenField) {
37160             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
37161         }
37162         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
37163     },
37164
37165     // private
37166     parseDate : function(value){
37167         if(!value || value instanceof Date){
37168             return value;
37169         }
37170         var v = Date.parseDate(value, this.format);
37171         if(!v && this.altFormats){
37172             if(!this.altFormatsArray){
37173                 this.altFormatsArray = this.altFormats.split("|");
37174             }
37175             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
37176                 v = Date.parseDate(value, this.altFormatsArray[i]);
37177             }
37178         }
37179         return v;
37180     },
37181
37182     // private
37183     formatDate : function(date, fmt){
37184         return (!date || !(date instanceof Date)) ?
37185                date : date.dateFormat(fmt || this.format);
37186     },
37187
37188     // private
37189     menuListeners : {
37190         select: function(m, d){
37191             this.setValue(d);
37192             this.fireEvent('select', this, d);
37193         },
37194         show : function(){ // retain focus styling
37195             this.onFocus();
37196         },
37197         hide : function(){
37198             this.focus.defer(10, this);
37199             var ml = this.menuListeners;
37200             this.menu.un("select", ml.select,  this);
37201             this.menu.un("show", ml.show,  this);
37202             this.menu.un("hide", ml.hide,  this);
37203         }
37204     },
37205
37206     // private
37207     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
37208     onTriggerClick : function(){
37209         if(this.disabled){
37210             return;
37211         }
37212         if(this.menu == null){
37213             this.menu = new Roo.menu.DateMenu();
37214         }
37215         Roo.apply(this.menu.picker,  {
37216             showClear: this.allowBlank,
37217             minDate : this.minValue,
37218             maxDate : this.maxValue,
37219             disabledDatesRE : this.ddMatch,
37220             disabledDatesText : this.disabledDatesText,
37221             disabledDays : this.disabledDays,
37222             disabledDaysText : this.disabledDaysText,
37223             format : this.format,
37224             minText : String.format(this.minText, this.formatDate(this.minValue)),
37225             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
37226         });
37227         this.menu.on(Roo.apply({}, this.menuListeners, {
37228             scope:this
37229         }));
37230         this.menu.picker.setValue(this.getValue() || new Date());
37231         this.menu.show(this.el, "tl-bl?");
37232     },
37233
37234     beforeBlur : function(){
37235         var v = this.parseDate(this.getRawValue());
37236         if(v){
37237             this.setValue(v);
37238         }
37239     }
37240
37241     /** @cfg {Boolean} grow @hide */
37242     /** @cfg {Number} growMin @hide */
37243     /** @cfg {Number} growMax @hide */
37244     /**
37245      * @hide
37246      * @method autoSize
37247      */
37248 });/*
37249  * Based on:
37250  * Ext JS Library 1.1.1
37251  * Copyright(c) 2006-2007, Ext JS, LLC.
37252  *
37253  * Originally Released Under LGPL - original licence link has changed is not relivant.
37254  *
37255  * Fork - LGPL
37256  * <script type="text/javascript">
37257  */
37258  
37259
37260 /**
37261  * @class Roo.form.ComboBox
37262  * @extends Roo.form.TriggerField
37263  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
37264  * @constructor
37265  * Create a new ComboBox.
37266  * @param {Object} config Configuration options
37267  */
37268 Roo.form.ComboBox = function(config){
37269     Roo.form.ComboBox.superclass.constructor.call(this, config);
37270     this.addEvents({
37271         /**
37272          * @event expand
37273          * Fires when the dropdown list is expanded
37274              * @param {Roo.form.ComboBox} combo This combo box
37275              */
37276         'expand' : true,
37277         /**
37278          * @event collapse
37279          * Fires when the dropdown list is collapsed
37280              * @param {Roo.form.ComboBox} combo This combo box
37281              */
37282         'collapse' : true,
37283         /**
37284          * @event beforeselect
37285          * Fires before a list item is selected. Return false to cancel the selection.
37286              * @param {Roo.form.ComboBox} combo This combo box
37287              * @param {Roo.data.Record} record The data record returned from the underlying store
37288              * @param {Number} index The index of the selected item in the dropdown list
37289              */
37290         'beforeselect' : true,
37291         /**
37292          * @event select
37293          * Fires when a list item is selected
37294              * @param {Roo.form.ComboBox} combo This combo box
37295              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37296              * @param {Number} index The index of the selected item in the dropdown list
37297              */
37298         'select' : true,
37299         /**
37300          * @event beforequery
37301          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37302          * The event object passed has these properties:
37303              * @param {Roo.form.ComboBox} combo This combo box
37304              * @param {String} query The query
37305              * @param {Boolean} forceAll true to force "all" query
37306              * @param {Boolean} cancel true to cancel the query
37307              * @param {Object} e The query event object
37308              */
37309         'beforequery': true,
37310          /**
37311          * @event add
37312          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37313              * @param {Roo.form.ComboBox} combo This combo box
37314              */
37315         'add' : true,
37316         /**
37317          * @event edit
37318          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37319              * @param {Roo.form.ComboBox} combo This combo box
37320              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37321              */
37322         'edit' : true
37323         
37324         
37325     });
37326     if(this.transform){
37327         this.allowDomMove = false;
37328         var s = Roo.getDom(this.transform);
37329         if(!this.hiddenName){
37330             this.hiddenName = s.name;
37331         }
37332         if(!this.store){
37333             this.mode = 'local';
37334             var d = [], opts = s.options;
37335             for(var i = 0, len = opts.length;i < len; i++){
37336                 var o = opts[i];
37337                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37338                 if(o.selected) {
37339                     this.value = value;
37340                 }
37341                 d.push([value, o.text]);
37342             }
37343             this.store = new Roo.data.SimpleStore({
37344                 'id': 0,
37345                 fields: ['value', 'text'],
37346                 data : d
37347             });
37348             this.valueField = 'value';
37349             this.displayField = 'text';
37350         }
37351         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37352         if(!this.lazyRender){
37353             this.target = true;
37354             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37355             s.parentNode.removeChild(s); // remove it
37356             this.render(this.el.parentNode);
37357         }else{
37358             s.parentNode.removeChild(s); // remove it
37359         }
37360
37361     }
37362     if (this.store) {
37363         this.store = Roo.factory(this.store, Roo.data);
37364     }
37365     
37366     this.selectedIndex = -1;
37367     if(this.mode == 'local'){
37368         if(config.queryDelay === undefined){
37369             this.queryDelay = 10;
37370         }
37371         if(config.minChars === undefined){
37372             this.minChars = 0;
37373         }
37374     }
37375 };
37376
37377 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37378     /**
37379      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37380      */
37381     /**
37382      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37383      * rendering into an Roo.Editor, defaults to false)
37384      */
37385     /**
37386      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37387      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37388      */
37389     /**
37390      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37391      */
37392     /**
37393      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37394      * the dropdown list (defaults to undefined, with no header element)
37395      */
37396
37397      /**
37398      * @cfg {String/Roo.Template} tpl The template to use to render the output
37399      */
37400      
37401     // private
37402     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37403     /**
37404      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37405      */
37406     listWidth: undefined,
37407     /**
37408      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37409      * mode = 'remote' or 'text' if mode = 'local')
37410      */
37411     displayField: undefined,
37412     /**
37413      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37414      * mode = 'remote' or 'value' if mode = 'local'). 
37415      * Note: use of a valueField requires the user make a selection
37416      * in order for a value to be mapped.
37417      */
37418     valueField: undefined,
37419     
37420     
37421     /**
37422      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37423      * field's data value (defaults to the underlying DOM element's name)
37424      */
37425     hiddenName: undefined,
37426     /**
37427      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37428      */
37429     listClass: '',
37430     /**
37431      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37432      */
37433     selectedClass: 'x-combo-selected',
37434     /**
37435      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37436      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37437      * which displays a downward arrow icon).
37438      */
37439     triggerClass : 'x-form-arrow-trigger',
37440     /**
37441      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37442      */
37443     shadow:'sides',
37444     /**
37445      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37446      * anchor positions (defaults to 'tl-bl')
37447      */
37448     listAlign: 'tl-bl?',
37449     /**
37450      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37451      */
37452     maxHeight: 300,
37453     /**
37454      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37455      * query specified by the allQuery config option (defaults to 'query')
37456      */
37457     triggerAction: 'query',
37458     /**
37459      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37460      * (defaults to 4, does not apply if editable = false)
37461      */
37462     minChars : 4,
37463     /**
37464      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37465      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37466      */
37467     typeAhead: false,
37468     /**
37469      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37470      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37471      */
37472     queryDelay: 500,
37473     /**
37474      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37475      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37476      */
37477     pageSize: 0,
37478     /**
37479      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37480      * when editable = true (defaults to false)
37481      */
37482     selectOnFocus:false,
37483     /**
37484      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37485      */
37486     queryParam: 'query',
37487     /**
37488      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37489      * when mode = 'remote' (defaults to 'Loading...')
37490      */
37491     loadingText: 'Loading...',
37492     /**
37493      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37494      */
37495     resizable: false,
37496     /**
37497      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37498      */
37499     handleHeight : 8,
37500     /**
37501      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37502      * traditional select (defaults to true)
37503      */
37504     editable: true,
37505     /**
37506      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37507      */
37508     allQuery: '',
37509     /**
37510      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37511      */
37512     mode: 'remote',
37513     /**
37514      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37515      * listWidth has a higher value)
37516      */
37517     minListWidth : 70,
37518     /**
37519      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37520      * allow the user to set arbitrary text into the field (defaults to false)
37521      */
37522     forceSelection:false,
37523     /**
37524      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37525      * if typeAhead = true (defaults to 250)
37526      */
37527     typeAheadDelay : 250,
37528     /**
37529      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37530      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37531      */
37532     valueNotFoundText : undefined,
37533     /**
37534      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37535      */
37536     blockFocus : false,
37537     
37538     /**
37539      * @cfg {Boolean} disableClear Disable showing of clear button.
37540      */
37541     disableClear : false,
37542     /**
37543      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37544      */
37545     alwaysQuery : false,
37546     
37547     //private
37548     addicon : false,
37549     editicon: false,
37550     
37551     // element that contains real text value.. (when hidden is used..)
37552      
37553     // private
37554     onRender : function(ct, position){
37555         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37556         if(this.hiddenName){
37557             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37558                     'before', true);
37559             this.hiddenField.value =
37560                 this.hiddenValue !== undefined ? this.hiddenValue :
37561                 this.value !== undefined ? this.value : '';
37562
37563             // prevent input submission
37564             this.el.dom.removeAttribute('name');
37565              
37566              
37567         }
37568         if(Roo.isGecko){
37569             this.el.dom.setAttribute('autocomplete', 'off');
37570         }
37571
37572         var cls = 'x-combo-list';
37573
37574         this.list = new Roo.Layer({
37575             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37576         });
37577
37578         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37579         this.list.setWidth(lw);
37580         this.list.swallowEvent('mousewheel');
37581         this.assetHeight = 0;
37582
37583         if(this.title){
37584             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37585             this.assetHeight += this.header.getHeight();
37586         }
37587
37588         this.innerList = this.list.createChild({cls:cls+'-inner'});
37589         this.innerList.on('mouseover', this.onViewOver, this);
37590         this.innerList.on('mousemove', this.onViewMove, this);
37591         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37592         
37593         if(this.allowBlank && !this.pageSize && !this.disableClear){
37594             this.footer = this.list.createChild({cls:cls+'-ft'});
37595             this.pageTb = new Roo.Toolbar(this.footer);
37596            
37597         }
37598         if(this.pageSize){
37599             this.footer = this.list.createChild({cls:cls+'-ft'});
37600             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37601                     {pageSize: this.pageSize});
37602             
37603         }
37604         
37605         if (this.pageTb && this.allowBlank && !this.disableClear) {
37606             var _this = this;
37607             this.pageTb.add(new Roo.Toolbar.Fill(), {
37608                 cls: 'x-btn-icon x-btn-clear',
37609                 text: '&#160;',
37610                 handler: function()
37611                 {
37612                     _this.collapse();
37613                     _this.clearValue();
37614                     _this.onSelect(false, -1);
37615                 }
37616             });
37617         }
37618         if (this.footer) {
37619             this.assetHeight += this.footer.getHeight();
37620         }
37621         
37622
37623         if(!this.tpl){
37624             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37625         }
37626
37627         this.view = new Roo.View(this.innerList, this.tpl, {
37628             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37629         });
37630
37631         this.view.on('click', this.onViewClick, this);
37632
37633         this.store.on('beforeload', this.onBeforeLoad, this);
37634         this.store.on('load', this.onLoad, this);
37635         this.store.on('loadexception', this.collapse, this);
37636
37637         if(this.resizable){
37638             this.resizer = new Roo.Resizable(this.list,  {
37639                pinned:true, handles:'se'
37640             });
37641             this.resizer.on('resize', function(r, w, h){
37642                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37643                 this.listWidth = w;
37644                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37645                 this.restrictHeight();
37646             }, this);
37647             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37648         }
37649         if(!this.editable){
37650             this.editable = true;
37651             this.setEditable(false);
37652         }  
37653         
37654         
37655         if (typeof(this.events.add.listeners) != 'undefined') {
37656             
37657             this.addicon = this.wrap.createChild(
37658                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37659        
37660             this.addicon.on('click', function(e) {
37661                 this.fireEvent('add', this);
37662             }, this);
37663         }
37664         if (typeof(this.events.edit.listeners) != 'undefined') {
37665             
37666             this.editicon = this.wrap.createChild(
37667                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37668             if (this.addicon) {
37669                 this.editicon.setStyle('margin-left', '40px');
37670             }
37671             this.editicon.on('click', function(e) {
37672                 
37673                 // we fire even  if inothing is selected..
37674                 this.fireEvent('edit', this, this.lastData );
37675                 
37676             }, this);
37677         }
37678         
37679         
37680         
37681     },
37682
37683     // private
37684     initEvents : function(){
37685         Roo.form.ComboBox.superclass.initEvents.call(this);
37686
37687         this.keyNav = new Roo.KeyNav(this.el, {
37688             "up" : function(e){
37689                 this.inKeyMode = true;
37690                 this.selectPrev();
37691             },
37692
37693             "down" : function(e){
37694                 if(!this.isExpanded()){
37695                     this.onTriggerClick();
37696                 }else{
37697                     this.inKeyMode = true;
37698                     this.selectNext();
37699                 }
37700             },
37701
37702             "enter" : function(e){
37703                 this.onViewClick();
37704                 //return true;
37705             },
37706
37707             "esc" : function(e){
37708                 this.collapse();
37709             },
37710
37711             "tab" : function(e){
37712                 this.onViewClick(false);
37713                 this.fireEvent("specialkey", this, e);
37714                 return true;
37715             },
37716
37717             scope : this,
37718
37719             doRelay : function(foo, bar, hname){
37720                 if(hname == 'down' || this.scope.isExpanded()){
37721                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37722                 }
37723                 return true;
37724             },
37725
37726             forceKeyDown: true
37727         });
37728         this.queryDelay = Math.max(this.queryDelay || 10,
37729                 this.mode == 'local' ? 10 : 250);
37730         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37731         if(this.typeAhead){
37732             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37733         }
37734         if(this.editable !== false){
37735             this.el.on("keyup", this.onKeyUp, this);
37736         }
37737         if(this.forceSelection){
37738             this.on('blur', this.doForce, this);
37739         }
37740     },
37741
37742     onDestroy : function(){
37743         if(this.view){
37744             this.view.setStore(null);
37745             this.view.el.removeAllListeners();
37746             this.view.el.remove();
37747             this.view.purgeListeners();
37748         }
37749         if(this.list){
37750             this.list.destroy();
37751         }
37752         if(this.store){
37753             this.store.un('beforeload', this.onBeforeLoad, this);
37754             this.store.un('load', this.onLoad, this);
37755             this.store.un('loadexception', this.collapse, this);
37756         }
37757         Roo.form.ComboBox.superclass.onDestroy.call(this);
37758     },
37759
37760     // private
37761     fireKey : function(e){
37762         if(e.isNavKeyPress() && !this.list.isVisible()){
37763             this.fireEvent("specialkey", this, e);
37764         }
37765     },
37766
37767     // private
37768     onResize: function(w, h){
37769         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37770         
37771         if(typeof w != 'number'){
37772             // we do not handle it!?!?
37773             return;
37774         }
37775         var tw = this.trigger.getWidth();
37776         tw += this.addicon ? this.addicon.getWidth() : 0;
37777         tw += this.editicon ? this.editicon.getWidth() : 0;
37778         var x = w - tw;
37779         this.el.setWidth( this.adjustWidth('input', x));
37780             
37781         this.trigger.setStyle('left', x+'px');
37782         
37783         if(this.list && this.listWidth === undefined){
37784             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37785             this.list.setWidth(lw);
37786             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37787         }
37788         
37789     
37790         
37791     },
37792
37793     /**
37794      * Allow or prevent the user from directly editing the field text.  If false is passed,
37795      * the user will only be able to select from the items defined in the dropdown list.  This method
37796      * is the runtime equivalent of setting the 'editable' config option at config time.
37797      * @param {Boolean} value True to allow the user to directly edit the field text
37798      */
37799     setEditable : function(value){
37800         if(value == this.editable){
37801             return;
37802         }
37803         this.editable = value;
37804         if(!value){
37805             this.el.dom.setAttribute('readOnly', true);
37806             this.el.on('mousedown', this.onTriggerClick,  this);
37807             this.el.addClass('x-combo-noedit');
37808         }else{
37809             this.el.dom.setAttribute('readOnly', false);
37810             this.el.un('mousedown', this.onTriggerClick,  this);
37811             this.el.removeClass('x-combo-noedit');
37812         }
37813     },
37814
37815     // private
37816     onBeforeLoad : function(){
37817         if(!this.hasFocus){
37818             return;
37819         }
37820         this.innerList.update(this.loadingText ?
37821                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37822         this.restrictHeight();
37823         this.selectedIndex = -1;
37824     },
37825
37826     // private
37827     onLoad : function(){
37828         if(!this.hasFocus){
37829             return;
37830         }
37831         if(this.store.getCount() > 0){
37832             this.expand();
37833             this.restrictHeight();
37834             if(this.lastQuery == this.allQuery){
37835                 if(this.editable){
37836                     this.el.dom.select();
37837                 }
37838                 if(!this.selectByValue(this.value, true)){
37839                     this.select(0, true);
37840                 }
37841             }else{
37842                 this.selectNext();
37843                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37844                     this.taTask.delay(this.typeAheadDelay);
37845                 }
37846             }
37847         }else{
37848             this.onEmptyResults();
37849         }
37850         //this.el.focus();
37851     },
37852
37853     // private
37854     onTypeAhead : function(){
37855         if(this.store.getCount() > 0){
37856             var r = this.store.getAt(0);
37857             var newValue = r.data[this.displayField];
37858             var len = newValue.length;
37859             var selStart = this.getRawValue().length;
37860             if(selStart != len){
37861                 this.setRawValue(newValue);
37862                 this.selectText(selStart, newValue.length);
37863             }
37864         }
37865     },
37866
37867     // private
37868     onSelect : function(record, index){
37869         if(this.fireEvent('beforeselect', this, record, index) !== false){
37870             this.setFromData(index > -1 ? record.data : false);
37871             this.collapse();
37872             this.fireEvent('select', this, record, index);
37873         }
37874     },
37875
37876     /**
37877      * Returns the currently selected field value or empty string if no value is set.
37878      * @return {String} value The selected value
37879      */
37880     getValue : function(){
37881         if(this.valueField){
37882             return typeof this.value != 'undefined' ? this.value : '';
37883         }else{
37884             return Roo.form.ComboBox.superclass.getValue.call(this);
37885         }
37886     },
37887
37888     /**
37889      * Clears any text/value currently set in the field
37890      */
37891     clearValue : function(){
37892         if(this.hiddenField){
37893             this.hiddenField.value = '';
37894         }
37895         this.value = '';
37896         this.setRawValue('');
37897         this.lastSelectionText = '';
37898         this.applyEmptyText();
37899     },
37900
37901     /**
37902      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37903      * will be displayed in the field.  If the value does not match the data value of an existing item,
37904      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37905      * Otherwise the field will be blank (although the value will still be set).
37906      * @param {String} value The value to match
37907      */
37908     setValue : function(v){
37909         var text = v;
37910         if(this.valueField){
37911             var r = this.findRecord(this.valueField, v);
37912             if(r){
37913                 text = r.data[this.displayField];
37914             }else if(this.valueNotFoundText !== undefined){
37915                 text = this.valueNotFoundText;
37916             }
37917         }
37918         this.lastSelectionText = text;
37919         if(this.hiddenField){
37920             this.hiddenField.value = v;
37921         }
37922         Roo.form.ComboBox.superclass.setValue.call(this, text);
37923         this.value = v;
37924     },
37925     /**
37926      * @property {Object} the last set data for the element
37927      */
37928     
37929     lastData : false,
37930     /**
37931      * Sets the value of the field based on a object which is related to the record format for the store.
37932      * @param {Object} value the value to set as. or false on reset?
37933      */
37934     setFromData : function(o){
37935         var dv = ''; // display value
37936         var vv = ''; // value value..
37937         this.lastData = o;
37938         if (this.displayField) {
37939             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37940         } else {
37941             // this is an error condition!!!
37942             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37943         }
37944         
37945         if(this.valueField){
37946             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37947         }
37948         if(this.hiddenField){
37949             this.hiddenField.value = vv;
37950             
37951             this.lastSelectionText = dv;
37952             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37953             this.value = vv;
37954             return;
37955         }
37956         // no hidden field.. - we store the value in 'value', but still display
37957         // display field!!!!
37958         this.lastSelectionText = dv;
37959         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37960         this.value = vv;
37961         
37962         
37963     },
37964     // private
37965     reset : function(){
37966         // overridden so that last data is reset..
37967         this.setValue(this.originalValue);
37968         this.clearInvalid();
37969         this.lastData = false;
37970     },
37971     // private
37972     findRecord : function(prop, value){
37973         var record;
37974         if(this.store.getCount() > 0){
37975             this.store.each(function(r){
37976                 if(r.data[prop] == value){
37977                     record = r;
37978                     return false;
37979                 }
37980                 return true;
37981             });
37982         }
37983         return record;
37984     },
37985     
37986     getName: function()
37987     {
37988         // returns hidden if it's set..
37989         if (!this.rendered) {return ''};
37990         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
37991         
37992     },
37993     // private
37994     onViewMove : function(e, t){
37995         this.inKeyMode = false;
37996     },
37997
37998     // private
37999     onViewOver : function(e, t){
38000         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38001             return;
38002         }
38003         var item = this.view.findItemFromChild(t);
38004         if(item){
38005             var index = this.view.indexOf(item);
38006             this.select(index, false);
38007         }
38008     },
38009
38010     // private
38011     onViewClick : function(doFocus)
38012     {
38013         var index = this.view.getSelectedIndexes()[0];
38014         var r = this.store.getAt(index);
38015         if(r){
38016             this.onSelect(r, index);
38017         }
38018         if(doFocus !== false && !this.blockFocus){
38019             this.el.focus();
38020         }
38021     },
38022
38023     // private
38024     restrictHeight : function(){
38025         this.innerList.dom.style.height = '';
38026         var inner = this.innerList.dom;
38027         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38028         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38029         this.list.beginUpdate();
38030         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38031         this.list.alignTo(this.el, this.listAlign);
38032         this.list.endUpdate();
38033     },
38034
38035     // private
38036     onEmptyResults : function(){
38037         this.collapse();
38038     },
38039
38040     /**
38041      * Returns true if the dropdown list is expanded, else false.
38042      */
38043     isExpanded : function(){
38044         return this.list.isVisible();
38045     },
38046
38047     /**
38048      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38049      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38050      * @param {String} value The data value of the item to select
38051      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38052      * selected item if it is not currently in view (defaults to true)
38053      * @return {Boolean} True if the value matched an item in the list, else false
38054      */
38055     selectByValue : function(v, scrollIntoView){
38056         if(v !== undefined && v !== null){
38057             var r = this.findRecord(this.valueField || this.displayField, v);
38058             if(r){
38059                 this.select(this.store.indexOf(r), scrollIntoView);
38060                 return true;
38061             }
38062         }
38063         return false;
38064     },
38065
38066     /**
38067      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38068      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38069      * @param {Number} index The zero-based index of the list item to select
38070      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38071      * selected item if it is not currently in view (defaults to true)
38072      */
38073     select : function(index, scrollIntoView){
38074         this.selectedIndex = index;
38075         this.view.select(index);
38076         if(scrollIntoView !== false){
38077             var el = this.view.getNode(index);
38078             if(el){
38079                 this.innerList.scrollChildIntoView(el, false);
38080             }
38081         }
38082     },
38083
38084     // private
38085     selectNext : function(){
38086         var ct = this.store.getCount();
38087         if(ct > 0){
38088             if(this.selectedIndex == -1){
38089                 this.select(0);
38090             }else if(this.selectedIndex < ct-1){
38091                 this.select(this.selectedIndex+1);
38092             }
38093         }
38094     },
38095
38096     // private
38097     selectPrev : function(){
38098         var ct = this.store.getCount();
38099         if(ct > 0){
38100             if(this.selectedIndex == -1){
38101                 this.select(0);
38102             }else if(this.selectedIndex != 0){
38103                 this.select(this.selectedIndex-1);
38104             }
38105         }
38106     },
38107
38108     // private
38109     onKeyUp : function(e){
38110         if(this.editable !== false && !e.isSpecialKey()){
38111             this.lastKey = e.getKey();
38112             this.dqTask.delay(this.queryDelay);
38113         }
38114     },
38115
38116     // private
38117     validateBlur : function(){
38118         return !this.list || !this.list.isVisible();   
38119     },
38120
38121     // private
38122     initQuery : function(){
38123         this.doQuery(this.getRawValue());
38124     },
38125
38126     // private
38127     doForce : function(){
38128         if(this.el.dom.value.length > 0){
38129             this.el.dom.value =
38130                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38131             this.applyEmptyText();
38132         }
38133     },
38134
38135     /**
38136      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38137      * query allowing the query action to be canceled if needed.
38138      * @param {String} query The SQL query to execute
38139      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38140      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38141      * saved in the current store (defaults to false)
38142      */
38143     doQuery : function(q, forceAll){
38144         if(q === undefined || q === null){
38145             q = '';
38146         }
38147         var qe = {
38148             query: q,
38149             forceAll: forceAll,
38150             combo: this,
38151             cancel:false
38152         };
38153         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38154             return false;
38155         }
38156         q = qe.query;
38157         forceAll = qe.forceAll;
38158         if(forceAll === true || (q.length >= this.minChars)){
38159             if(this.lastQuery != q || this.alwaysQuery){
38160                 this.lastQuery = q;
38161                 if(this.mode == 'local'){
38162                     this.selectedIndex = -1;
38163                     if(forceAll){
38164                         this.store.clearFilter();
38165                     }else{
38166                         this.store.filter(this.displayField, q);
38167                     }
38168                     this.onLoad();
38169                 }else{
38170                     this.store.baseParams[this.queryParam] = q;
38171                     this.store.load({
38172                         params: this.getParams(q)
38173                     });
38174                     this.expand();
38175                 }
38176             }else{
38177                 this.selectedIndex = -1;
38178                 this.onLoad();   
38179             }
38180         }
38181     },
38182
38183     // private
38184     getParams : function(q){
38185         var p = {};
38186         //p[this.queryParam] = q;
38187         if(this.pageSize){
38188             p.start = 0;
38189             p.limit = this.pageSize;
38190         }
38191         return p;
38192     },
38193
38194     /**
38195      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38196      */
38197     collapse : function(){
38198         if(!this.isExpanded()){
38199             return;
38200         }
38201         this.list.hide();
38202         Roo.get(document).un('mousedown', this.collapseIf, this);
38203         Roo.get(document).un('mousewheel', this.collapseIf, this);
38204         if (!this.editable) {
38205             Roo.get(document).un('keydown', this.listKeyPress, this);
38206         }
38207         this.fireEvent('collapse', this);
38208     },
38209
38210     // private
38211     collapseIf : function(e){
38212         if(!e.within(this.wrap) && !e.within(this.list)){
38213             this.collapse();
38214         }
38215     },
38216
38217     /**
38218      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
38219      */
38220     expand : function(){
38221         if(this.isExpanded() || !this.hasFocus){
38222             return;
38223         }
38224         this.list.alignTo(this.el, this.listAlign);
38225         this.list.show();
38226         Roo.get(document).on('mousedown', this.collapseIf, this);
38227         Roo.get(document).on('mousewheel', this.collapseIf, this);
38228         if (!this.editable) {
38229             Roo.get(document).on('keydown', this.listKeyPress, this);
38230         }
38231         
38232         this.fireEvent('expand', this);
38233     },
38234
38235     // private
38236     // Implements the default empty TriggerField.onTriggerClick function
38237     onTriggerClick : function(){
38238         if(this.disabled){
38239             return;
38240         }
38241         if(this.isExpanded()){
38242             this.collapse();
38243             if (!this.blockFocus) {
38244                 this.el.focus();
38245             }
38246             
38247         }else {
38248             this.hasFocus = true;
38249             if(this.triggerAction == 'all') {
38250                 this.doQuery(this.allQuery, true);
38251             } else {
38252                 this.doQuery(this.getRawValue());
38253             }
38254             if (!this.blockFocus) {
38255                 this.el.focus();
38256             }
38257         }
38258     },
38259     listKeyPress : function(e)
38260     {
38261         //Roo.log('listkeypress');
38262         // scroll to first matching element based on key pres..
38263         if (e.isSpecialKey()) {
38264             return false;
38265         }
38266         var k = String.fromCharCode(e.getKey()).toUpperCase();
38267         //Roo.log(k);
38268         var match  = false;
38269         var csel = this.view.getSelectedNodes();
38270         var cselitem = false;
38271         if (csel.length) {
38272             var ix = this.view.indexOf(csel[0]);
38273             cselitem  = this.store.getAt(ix);
38274             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
38275                 cselitem = false;
38276             }
38277             
38278         }
38279         
38280         this.store.each(function(v) { 
38281             if (cselitem) {
38282                 // start at existing selection.
38283                 if (cselitem.id == v.id) {
38284                     cselitem = false;
38285                 }
38286                 return;
38287             }
38288                 
38289             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
38290                 match = this.store.indexOf(v);
38291                 return false;
38292             }
38293         }, this);
38294         
38295         if (match === false) {
38296             return true; // no more action?
38297         }
38298         // scroll to?
38299         this.view.select(match);
38300         var sn = Roo.get(this.view.getSelectedNodes()[0])
38301         sn.scrollIntoView(sn.dom.parentNode, false);
38302     }
38303
38304     /** 
38305     * @cfg {Boolean} grow 
38306     * @hide 
38307     */
38308     /** 
38309     * @cfg {Number} growMin 
38310     * @hide 
38311     */
38312     /** 
38313     * @cfg {Number} growMax 
38314     * @hide 
38315     */
38316     /**
38317      * @hide
38318      * @method autoSize
38319      */
38320 });/*
38321  * Based on:
38322  * Ext JS Library 1.1.1
38323  * Copyright(c) 2006-2007, Ext JS, LLC.
38324  *
38325  * Originally Released Under LGPL - original licence link has changed is not relivant.
38326  *
38327  * Fork - LGPL
38328  * <script type="text/javascript">
38329  */
38330 /**
38331  * @class Roo.form.Checkbox
38332  * @extends Roo.form.Field
38333  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38334  * @constructor
38335  * Creates a new Checkbox
38336  * @param {Object} config Configuration options
38337  */
38338 Roo.form.Checkbox = function(config){
38339     Roo.form.Checkbox.superclass.constructor.call(this, config);
38340     this.addEvents({
38341         /**
38342          * @event check
38343          * Fires when the checkbox is checked or unchecked.
38344              * @param {Roo.form.Checkbox} this This checkbox
38345              * @param {Boolean} checked The new checked value
38346              */
38347         check : true
38348     });
38349 };
38350
38351 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38352     /**
38353      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38354      */
38355     focusClass : undefined,
38356     /**
38357      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38358      */
38359     fieldClass: "x-form-field",
38360     /**
38361      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38362      */
38363     checked: false,
38364     /**
38365      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38366      * {tag: "input", type: "checkbox", autocomplete: "off"})
38367      */
38368     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38369     /**
38370      * @cfg {String} boxLabel The text that appears beside the checkbox
38371      */
38372     boxLabel : "",
38373     /**
38374      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38375      */  
38376     inputValue : '1',
38377     /**
38378      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38379      */
38380      valueOff: '0', // value when not checked..
38381
38382     actionMode : 'viewEl', 
38383     //
38384     // private
38385     itemCls : 'x-menu-check-item x-form-item',
38386     groupClass : 'x-menu-group-item',
38387     inputType : 'hidden',
38388     
38389     
38390     inSetChecked: false, // check that we are not calling self...
38391     
38392     inputElement: false, // real input element?
38393     basedOn: false, // ????
38394     
38395     isFormField: true, // not sure where this is needed!!!!
38396
38397     onResize : function(){
38398         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38399         if(!this.boxLabel){
38400             this.el.alignTo(this.wrap, 'c-c');
38401         }
38402     },
38403
38404     initEvents : function(){
38405         Roo.form.Checkbox.superclass.initEvents.call(this);
38406         this.el.on("click", this.onClick,  this);
38407         this.el.on("change", this.onClick,  this);
38408     },
38409
38410
38411     getResizeEl : function(){
38412         return this.wrap;
38413     },
38414
38415     getPositionEl : function(){
38416         return this.wrap;
38417     },
38418
38419     // private
38420     onRender : function(ct, position){
38421         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38422         /*
38423         if(this.inputValue !== undefined){
38424             this.el.dom.value = this.inputValue;
38425         }
38426         */
38427         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38428         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38429         var viewEl = this.wrap.createChild({ 
38430             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38431         this.viewEl = viewEl;   
38432         this.wrap.on('click', this.onClick,  this); 
38433         
38434         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38435         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38436         
38437         
38438         
38439         if(this.boxLabel){
38440             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38441         //    viewEl.on('click', this.onClick,  this); 
38442         }
38443         //if(this.checked){
38444             this.setChecked(this.checked);
38445         //}else{
38446             //this.checked = this.el.dom;
38447         //}
38448
38449     },
38450
38451     // private
38452     initValue : Roo.emptyFn,
38453
38454     /**
38455      * Returns the checked state of the checkbox.
38456      * @return {Boolean} True if checked, else false
38457      */
38458     getValue : function(){
38459         if(this.el){
38460             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38461         }
38462         return this.valueOff;
38463         
38464     },
38465
38466         // private
38467     onClick : function(){ 
38468         this.setChecked(!this.checked);
38469
38470         //if(this.el.dom.checked != this.checked){
38471         //    this.setValue(this.el.dom.checked);
38472        // }
38473     },
38474
38475     /**
38476      * Sets the checked state of the checkbox.
38477      * On is always based on a string comparison between inputValue and the param.
38478      * @param {Boolean/String} value - the value to set 
38479      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38480      */
38481     setValue : function(v,suppressEvent){
38482         
38483         
38484         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38485         //if(this.el && this.el.dom){
38486         //    this.el.dom.checked = this.checked;
38487         //    this.el.dom.defaultChecked = this.checked;
38488         //}
38489         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38490         //this.fireEvent("check", this, this.checked);
38491     },
38492     // private..
38493     setChecked : function(state,suppressEvent)
38494     {
38495         if (this.inSetChecked) {
38496             this.checked = state;
38497             return;
38498         }
38499         
38500     
38501         if(this.wrap){
38502             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38503         }
38504         this.checked = state;
38505         if(suppressEvent !== true){
38506             this.fireEvent('check', this, state);
38507         }
38508         this.inSetChecked = true;
38509         this.el.dom.value = state ? this.inputValue : this.valueOff;
38510         this.inSetChecked = false;
38511         
38512     },
38513     // handle setting of hidden value by some other method!!?!?
38514     setFromHidden: function()
38515     {
38516         if(!this.el){
38517             return;
38518         }
38519         //console.log("SET FROM HIDDEN");
38520         //alert('setFrom hidden');
38521         this.setValue(this.el.dom.value);
38522     },
38523     
38524     onDestroy : function()
38525     {
38526         if(this.viewEl){
38527             Roo.get(this.viewEl).remove();
38528         }
38529          
38530         Roo.form.Checkbox.superclass.onDestroy.call(this);
38531     }
38532
38533 });/*
38534  * Based on:
38535  * Ext JS Library 1.1.1
38536  * Copyright(c) 2006-2007, Ext JS, LLC.
38537  *
38538  * Originally Released Under LGPL - original licence link has changed is not relivant.
38539  *
38540  * Fork - LGPL
38541  * <script type="text/javascript">
38542  */
38543  
38544 /**
38545  * @class Roo.form.Radio
38546  * @extends Roo.form.Checkbox
38547  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38548  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38549  * @constructor
38550  * Creates a new Radio
38551  * @param {Object} config Configuration options
38552  */
38553 Roo.form.Radio = function(){
38554     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38555 };
38556 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38557     inputType: 'radio',
38558
38559     /**
38560      * If this radio is part of a group, it will return the selected value
38561      * @return {String}
38562      */
38563     getGroupValue : function(){
38564         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38565     }
38566 });//<script type="text/javascript">
38567
38568 /*
38569  * Ext JS Library 1.1.1
38570  * Copyright(c) 2006-2007, Ext JS, LLC.
38571  * licensing@extjs.com
38572  * 
38573  * http://www.extjs.com/license
38574  */
38575  
38576  /*
38577   * 
38578   * Known bugs:
38579   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38580   * - IE ? - no idea how much works there.
38581   * 
38582   * 
38583   * 
38584   */
38585  
38586
38587 /**
38588  * @class Ext.form.HtmlEditor
38589  * @extends Ext.form.Field
38590  * Provides a lightweight HTML Editor component.
38591  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38592  * 
38593  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38594  * supported by this editor.</b><br/><br/>
38595  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38596  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38597  */
38598 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38599       /**
38600      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38601      */
38602     toolbars : false,
38603     /**
38604      * @cfg {String} createLinkText The default text for the create link prompt
38605      */
38606     createLinkText : 'Please enter the URL for the link:',
38607     /**
38608      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38609      */
38610     defaultLinkValue : 'http:/'+'/',
38611    
38612      /**
38613      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
38614      *                        Roo.resizable.
38615      */
38616     resizable : false,
38617      /**
38618      * @cfg {Number} height (in pixels)
38619      */   
38620     height: 300,
38621    /**
38622      * @cfg {Number} width (in pixels)
38623      */   
38624     width: 500,
38625     
38626     /**
38627      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
38628      * 
38629      */
38630     stylesheets: false,
38631     
38632     // id of frame..
38633     frameId: false,
38634     
38635     // private properties
38636     validationEvent : false,
38637     deferHeight: true,
38638     initialized : false,
38639     activated : false,
38640     sourceEditMode : false,
38641     onFocus : Roo.emptyFn,
38642     iframePad:3,
38643     hideMode:'offsets',
38644     
38645     defaultAutoCreate : { // modified by initCompnoent..
38646         tag: "textarea",
38647         style:"width:500px;height:300px;",
38648         autocomplete: "off"
38649     },
38650
38651     // private
38652     initComponent : function(){
38653         this.addEvents({
38654             /**
38655              * @event initialize
38656              * Fires when the editor is fully initialized (including the iframe)
38657              * @param {HtmlEditor} this
38658              */
38659             initialize: true,
38660             /**
38661              * @event activate
38662              * Fires when the editor is first receives the focus. Any insertion must wait
38663              * until after this event.
38664              * @param {HtmlEditor} this
38665              */
38666             activate: true,
38667              /**
38668              * @event beforesync
38669              * Fires before the textarea is updated with content from the editor iframe. Return false
38670              * to cancel the sync.
38671              * @param {HtmlEditor} this
38672              * @param {String} html
38673              */
38674             beforesync: true,
38675              /**
38676              * @event beforepush
38677              * Fires before the iframe editor is updated with content from the textarea. Return false
38678              * to cancel the push.
38679              * @param {HtmlEditor} this
38680              * @param {String} html
38681              */
38682             beforepush: true,
38683              /**
38684              * @event sync
38685              * Fires when the textarea is updated with content from the editor iframe.
38686              * @param {HtmlEditor} this
38687              * @param {String} html
38688              */
38689             sync: true,
38690              /**
38691              * @event push
38692              * Fires when the iframe editor is updated with content from the textarea.
38693              * @param {HtmlEditor} this
38694              * @param {String} html
38695              */
38696             push: true,
38697              /**
38698              * @event editmodechange
38699              * Fires when the editor switches edit modes
38700              * @param {HtmlEditor} this
38701              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38702              */
38703             editmodechange: true,
38704             /**
38705              * @event editorevent
38706              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38707              * @param {HtmlEditor} this
38708              */
38709             editorevent: true
38710         });
38711         this.defaultAutoCreate =  {
38712             tag: "textarea",
38713             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
38714             autocomplete: "off"
38715         };
38716     },
38717
38718     /**
38719      * Protected method that will not generally be called directly. It
38720      * is called when the editor creates its toolbar. Override this method if you need to
38721      * add custom toolbar buttons.
38722      * @param {HtmlEditor} editor
38723      */
38724     createToolbar : function(editor){
38725         if (!editor.toolbars || !editor.toolbars.length) {
38726             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38727         }
38728         
38729         for (var i =0 ; i < editor.toolbars.length;i++) {
38730             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38731             editor.toolbars[i].init(editor);
38732         }
38733          
38734         
38735     },
38736
38737     /**
38738      * Protected method that will not generally be called directly. It
38739      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38740      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38741      */
38742     getDocMarkup : function(){
38743         // body styles..
38744         var st = '';
38745         if (this.stylesheets === false) {
38746             
38747             Roo.get(document.head).select('style').each(function(node) {
38748                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38749             });
38750             
38751             Roo.get(document.head).select('link').each(function(node) { 
38752                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
38753             });
38754             
38755         } else if (!this.stylesheets.length) {
38756                 // simple..
38757                 st = '<style type="text/css">' +
38758                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38759                    '</style>';
38760         } else {
38761             Roo.each(this.stylesheets, function(s) {
38762                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
38763             });
38764             
38765         }
38766         
38767         return '<html><head>' + st  +
38768             //<style type="text/css">' +
38769             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
38770             //'</style>' +
38771             ' </head><body></body></html>';
38772     },
38773
38774     // private
38775     onRender : function(ct, position)
38776     {
38777         var _t = this;
38778         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38779         this.el.dom.style.border = '0 none';
38780         this.el.dom.setAttribute('tabIndex', -1);
38781         this.el.addClass('x-hidden');
38782         if(Roo.isIE){ // fix IE 1px bogus margin
38783             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38784         }
38785         this.wrap = this.el.wrap({
38786             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38787         });
38788         
38789         if (this.resizable) {
38790             this.resizeEl = new Roo.Resizable(this.wrap, {
38791                 pinned : true,
38792                 wrap: true,
38793                 dynamic : true,
38794                 minHeight : this.height,
38795                 height: this.height,
38796                 handles : this.resizable,
38797                 width: this.width,
38798                 listeners : {
38799                     resize : function(r, w, h) {
38800                         _t.onResize(w,h); // -something
38801                     }
38802                 }
38803             });
38804             
38805         }
38806
38807         this.frameId = Roo.id();
38808         
38809         this.createToolbar(this);
38810         
38811       
38812         
38813         var iframe = this.wrap.createChild({
38814             tag: 'iframe',
38815             id: this.frameId,
38816             name: this.frameId,
38817             frameBorder : 'no',
38818             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38819         }, this.el
38820         );
38821         
38822        // console.log(iframe);
38823         //this.wrap.dom.appendChild(iframe);
38824
38825         this.iframe = iframe.dom;
38826
38827          this.assignDocWin();
38828         
38829         this.doc.designMode = 'on';
38830        
38831         this.doc.open();
38832         this.doc.write(this.getDocMarkup());
38833         this.doc.close();
38834
38835         
38836         var task = { // must defer to wait for browser to be ready
38837             run : function(){
38838                 //console.log("run task?" + this.doc.readyState);
38839                 this.assignDocWin();
38840                 if(this.doc.body || this.doc.readyState == 'complete'){
38841                     try {
38842                         this.doc.designMode="on";
38843                     } catch (e) {
38844                         return;
38845                     }
38846                     Roo.TaskMgr.stop(task);
38847                     this.initEditor.defer(10, this);
38848                 }
38849             },
38850             interval : 10,
38851             duration:10000,
38852             scope: this
38853         };
38854         Roo.TaskMgr.start(task);
38855
38856         if(!this.width){
38857             this.setSize(this.wrap.getSize());
38858         }
38859         if (this.resizeEl) {
38860             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
38861             // should trigger onReize..
38862         }
38863     },
38864
38865     // private
38866     onResize : function(w, h)
38867     {
38868         //Roo.log('resize: ' +w + ',' + h );
38869         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38870         if(this.el && this.iframe){
38871             if(typeof w == 'number'){
38872                 var aw = w - this.wrap.getFrameWidth('lr');
38873                 this.el.setWidth(this.adjustWidth('textarea', aw));
38874                 this.iframe.style.width = aw + 'px';
38875             }
38876             if(typeof h == 'number'){
38877                 var tbh = 0;
38878                 for (var i =0; i < this.toolbars.length;i++) {
38879                     // fixme - ask toolbars for heights?
38880                     tbh += this.toolbars[i].tb.el.getHeight();
38881                     if (this.toolbars[i].footer) {
38882                         tbh += this.toolbars[i].footer.el.getHeight();
38883                     }
38884                 }
38885                 
38886                 
38887                 
38888                 
38889                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38890                 ah -= 5; // knock a few pixes off for look..
38891                 this.el.setHeight(this.adjustWidth('textarea', ah));
38892                 this.iframe.style.height = ah + 'px';
38893                 if(this.doc){
38894                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38895                 }
38896             }
38897         }
38898     },
38899
38900     /**
38901      * Toggles the editor between standard and source edit mode.
38902      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38903      */
38904     toggleSourceEdit : function(sourceEditMode){
38905         
38906         this.sourceEditMode = sourceEditMode === true;
38907         
38908         if(this.sourceEditMode){
38909           
38910             this.syncValue();
38911             this.iframe.className = 'x-hidden';
38912             this.el.removeClass('x-hidden');
38913             this.el.dom.removeAttribute('tabIndex');
38914             this.el.focus();
38915         }else{
38916              
38917             this.pushValue();
38918             this.iframe.className = '';
38919             this.el.addClass('x-hidden');
38920             this.el.dom.setAttribute('tabIndex', -1);
38921             this.deferFocus();
38922         }
38923         this.setSize(this.wrap.getSize());
38924         this.fireEvent('editmodechange', this, this.sourceEditMode);
38925     },
38926
38927     // private used internally
38928     createLink : function(){
38929         var url = prompt(this.createLinkText, this.defaultLinkValue);
38930         if(url && url != 'http:/'+'/'){
38931             this.relayCmd('createlink', url);
38932         }
38933     },
38934
38935     // private (for BoxComponent)
38936     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38937
38938     // private (for BoxComponent)
38939     getResizeEl : function(){
38940         return this.wrap;
38941     },
38942
38943     // private (for BoxComponent)
38944     getPositionEl : function(){
38945         return this.wrap;
38946     },
38947
38948     // private
38949     initEvents : function(){
38950         this.originalValue = this.getValue();
38951     },
38952
38953     /**
38954      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38955      * @method
38956      */
38957     markInvalid : Roo.emptyFn,
38958     /**
38959      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38960      * @method
38961      */
38962     clearInvalid : Roo.emptyFn,
38963
38964     setValue : function(v){
38965         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38966         this.pushValue();
38967     },
38968
38969     /**
38970      * Protected method that will not generally be called directly. If you need/want
38971      * custom HTML cleanup, this is the method you should override.
38972      * @param {String} html The HTML to be cleaned
38973      * return {String} The cleaned HTML
38974      */
38975     cleanHtml : function(html){
38976         html = String(html);
38977         if(html.length > 5){
38978             if(Roo.isSafari){ // strip safari nonsense
38979                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38980             }
38981         }
38982         if(html == '&nbsp;'){
38983             html = '';
38984         }
38985         return html;
38986     },
38987
38988     /**
38989      * Protected method that will not generally be called directly. Syncs the contents
38990      * of the editor iframe with the textarea.
38991      */
38992     syncValue : function(){
38993         if(this.initialized){
38994             var bd = (this.doc.body || this.doc.documentElement);
38995             this.cleanUpPaste();
38996             var html = bd.innerHTML;
38997             if(Roo.isSafari){
38998                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38999                 var m = bs.match(/text-align:(.*?);/i);
39000                 if(m && m[1]){
39001                     html = '<div style="'+m[0]+'">' + html + '</div>';
39002                 }
39003             }
39004             html = this.cleanHtml(html);
39005             if(this.fireEvent('beforesync', this, html) !== false){
39006                 this.el.dom.value = html;
39007                 this.fireEvent('sync', this, html);
39008             }
39009         }
39010     },
39011
39012     /**
39013      * Protected method that will not generally be called directly. Pushes the value of the textarea
39014      * into the iframe editor.
39015      */
39016     pushValue : function(){
39017         if(this.initialized){
39018             var v = this.el.dom.value;
39019             if(v.length < 1){
39020                 v = '&#160;';
39021             }
39022             
39023             if(this.fireEvent('beforepush', this, v) !== false){
39024                 var d = (this.doc.body || this.doc.documentElement);
39025                 d.innerHTML = v;
39026                 this.cleanUpPaste();
39027                 this.el.dom.value = d.innerHTML;
39028                 this.fireEvent('push', this, v);
39029             }
39030         }
39031     },
39032
39033     // private
39034     deferFocus : function(){
39035         this.focus.defer(10, this);
39036     },
39037
39038     // doc'ed in Field
39039     focus : function(){
39040         if(this.win && !this.sourceEditMode){
39041             this.win.focus();
39042         }else{
39043             this.el.focus();
39044         }
39045     },
39046     
39047     assignDocWin: function()
39048     {
39049         var iframe = this.iframe;
39050         
39051          if(Roo.isIE){
39052             this.doc = iframe.contentWindow.document;
39053             this.win = iframe.contentWindow;
39054         } else {
39055             if (!Roo.get(this.frameId)) {
39056                 return;
39057             }
39058             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
39059             this.win = Roo.get(this.frameId).dom.contentWindow;
39060         }
39061     },
39062     
39063     // private
39064     initEditor : function(){
39065         //console.log("INIT EDITOR");
39066         this.assignDocWin();
39067         
39068         
39069         
39070         this.doc.designMode="on";
39071         this.doc.open();
39072         this.doc.write(this.getDocMarkup());
39073         this.doc.close();
39074         
39075         var dbody = (this.doc.body || this.doc.documentElement);
39076         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
39077         // this copies styles from the containing element into thsi one..
39078         // not sure why we need all of this..
39079         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
39080         ss['background-attachment'] = 'fixed'; // w3c
39081         dbody.bgProperties = 'fixed'; // ie
39082         Roo.DomHelper.applyStyles(dbody, ss);
39083         Roo.EventManager.on(this.doc, {
39084             //'mousedown': this.onEditorEvent,
39085             'mouseup': this.onEditorEvent,
39086             'dblclick': this.onEditorEvent,
39087             'click': this.onEditorEvent,
39088             'keyup': this.onEditorEvent,
39089             buffer:100,
39090             scope: this
39091         });
39092         if(Roo.isGecko){
39093             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
39094         }
39095         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
39096             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
39097         }
39098         this.initialized = true;
39099
39100         this.fireEvent('initialize', this);
39101         this.pushValue();
39102     },
39103
39104     // private
39105     onDestroy : function(){
39106         
39107         
39108         
39109         if(this.rendered){
39110             
39111             for (var i =0; i < this.toolbars.length;i++) {
39112                 // fixme - ask toolbars for heights?
39113                 this.toolbars[i].onDestroy();
39114             }
39115             
39116             this.wrap.dom.innerHTML = '';
39117             this.wrap.remove();
39118         }
39119     },
39120
39121     // private
39122     onFirstFocus : function(){
39123         
39124         this.assignDocWin();
39125         
39126         
39127         this.activated = true;
39128         for (var i =0; i < this.toolbars.length;i++) {
39129             this.toolbars[i].onFirstFocus();
39130         }
39131        
39132         if(Roo.isGecko){ // prevent silly gecko errors
39133             this.win.focus();
39134             var s = this.win.getSelection();
39135             if(!s.focusNode || s.focusNode.nodeType != 3){
39136                 var r = s.getRangeAt(0);
39137                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
39138                 r.collapse(true);
39139                 this.deferFocus();
39140             }
39141             try{
39142                 this.execCmd('useCSS', true);
39143                 this.execCmd('styleWithCSS', false);
39144             }catch(e){}
39145         }
39146         this.fireEvent('activate', this);
39147     },
39148
39149     // private
39150     adjustFont: function(btn){
39151         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
39152         //if(Roo.isSafari){ // safari
39153         //    adjust *= 2;
39154        // }
39155         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
39156         if(Roo.isSafari){ // safari
39157             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
39158             v =  (v < 10) ? 10 : v;
39159             v =  (v > 48) ? 48 : v;
39160             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
39161             
39162         }
39163         
39164         
39165         v = Math.max(1, v+adjust);
39166         
39167         this.execCmd('FontSize', v  );
39168     },
39169
39170     onEditorEvent : function(e){
39171         this.fireEvent('editorevent', this, e);
39172       //  this.updateToolbar();
39173         this.syncValue();
39174     },
39175
39176     insertTag : function(tg)
39177     {
39178         // could be a bit smarter... -> wrap the current selected tRoo..
39179         
39180         this.execCmd("formatblock",   tg);
39181         
39182     },
39183     
39184     insertText : function(txt)
39185     {
39186         
39187         
39188         range = this.createRange();
39189         range.deleteContents();
39190                //alert(Sender.getAttribute('label'));
39191                
39192         range.insertNode(this.doc.createTextNode(txt));
39193     } ,
39194     
39195     // private
39196     relayBtnCmd : function(btn){
39197         this.relayCmd(btn.cmd);
39198     },
39199
39200     /**
39201      * Executes a Midas editor command on the editor document and performs necessary focus and
39202      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
39203      * @param {String} cmd The Midas command
39204      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39205      */
39206     relayCmd : function(cmd, value){
39207         this.win.focus();
39208         this.execCmd(cmd, value);
39209         this.fireEvent('editorevent', this);
39210         //this.updateToolbar();
39211         this.deferFocus();
39212     },
39213
39214     /**
39215      * Executes a Midas editor command directly on the editor document.
39216      * For visual commands, you should use {@link #relayCmd} instead.
39217      * <b>This should only be called after the editor is initialized.</b>
39218      * @param {String} cmd The Midas command
39219      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
39220      */
39221     execCmd : function(cmd, value){
39222         this.doc.execCommand(cmd, false, value === undefined ? null : value);
39223         this.syncValue();
39224     },
39225
39226    
39227     /**
39228      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
39229      * to insert tRoo.
39230      * @param {String} text
39231      */
39232     insertAtCursor : function(text){
39233         if(!this.activated){
39234             return;
39235         }
39236         if(Roo.isIE){
39237             this.win.focus();
39238             var r = this.doc.selection.createRange();
39239             if(r){
39240                 r.collapse(true);
39241                 r.pasteHTML(text);
39242                 this.syncValue();
39243                 this.deferFocus();
39244             }
39245         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
39246             this.win.focus();
39247             this.execCmd('InsertHTML', text);
39248             this.deferFocus();
39249         }
39250     },
39251  // private
39252     mozKeyPress : function(e){
39253         if(e.ctrlKey){
39254             var c = e.getCharCode(), cmd;
39255           
39256             if(c > 0){
39257                 c = String.fromCharCode(c).toLowerCase();
39258                 switch(c){
39259                     case 'b':
39260                         cmd = 'bold';
39261                     break;
39262                     case 'i':
39263                         cmd = 'italic';
39264                     break;
39265                     case 'u':
39266                         cmd = 'underline';
39267                     case 'v':
39268                         this.cleanUpPaste.defer(100, this);
39269                         return;
39270                     break;
39271                 }
39272                 if(cmd){
39273                     this.win.focus();
39274                     this.execCmd(cmd);
39275                     this.deferFocus();
39276                     e.preventDefault();
39277                 }
39278                 
39279             }
39280         }
39281     },
39282
39283     // private
39284     fixKeys : function(){ // load time branching for fastest keydown performance
39285         if(Roo.isIE){
39286             return function(e){
39287                 var k = e.getKey(), r;
39288                 if(k == e.TAB){
39289                     e.stopEvent();
39290                     r = this.doc.selection.createRange();
39291                     if(r){
39292                         r.collapse(true);
39293                         r.pasteHTML('&#160;&#160;&#160;&#160;');
39294                         this.deferFocus();
39295                     }
39296                     return;
39297                 }
39298                 
39299                 if(k == e.ENTER){
39300                     r = this.doc.selection.createRange();
39301                     if(r){
39302                         var target = r.parentElement();
39303                         if(!target || target.tagName.toLowerCase() != 'li'){
39304                             e.stopEvent();
39305                             r.pasteHTML('<br />');
39306                             r.collapse(false);
39307                             r.select();
39308                         }
39309                     }
39310                 }
39311                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39312                     this.cleanUpPaste.defer(100, this);
39313                     return;
39314                 }
39315                 
39316                 
39317             };
39318         }else if(Roo.isOpera){
39319             return function(e){
39320                 var k = e.getKey();
39321                 if(k == e.TAB){
39322                     e.stopEvent();
39323                     this.win.focus();
39324                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
39325                     this.deferFocus();
39326                 }
39327                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39328                     this.cleanUpPaste.defer(100, this);
39329                     return;
39330                 }
39331                 
39332             };
39333         }else if(Roo.isSafari){
39334             return function(e){
39335                 var k = e.getKey();
39336                 
39337                 if(k == e.TAB){
39338                     e.stopEvent();
39339                     this.execCmd('InsertText','\t');
39340                     this.deferFocus();
39341                     return;
39342                 }
39343                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
39344                     this.cleanUpPaste.defer(100, this);
39345                     return;
39346                 }
39347                 
39348              };
39349         }
39350     }(),
39351     
39352     getAllAncestors: function()
39353     {
39354         var p = this.getSelectedNode();
39355         var a = [];
39356         if (!p) {
39357             a.push(p); // push blank onto stack..
39358             p = this.getParentElement();
39359         }
39360         
39361         
39362         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
39363             a.push(p);
39364             p = p.parentNode;
39365         }
39366         a.push(this.doc.body);
39367         return a;
39368     },
39369     lastSel : false,
39370     lastSelNode : false,
39371     
39372     
39373     getSelection : function() 
39374     {
39375         this.assignDocWin();
39376         return Roo.isIE ? this.doc.selection : this.win.getSelection();
39377     },
39378     
39379     getSelectedNode: function() 
39380     {
39381         // this may only work on Gecko!!!
39382         
39383         // should we cache this!!!!
39384         
39385         
39386         
39387          
39388         var range = this.createRange(this.getSelection()).cloneRange();
39389         
39390         if (Roo.isIE) {
39391             var parent = range.parentElement();
39392             while (true) {
39393                 var testRange = range.duplicate();
39394                 testRange.moveToElementText(parent);
39395                 if (testRange.inRange(range)) {
39396                     break;
39397                 }
39398                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
39399                     break;
39400                 }
39401                 parent = parent.parentElement;
39402             }
39403             return parent;
39404         }
39405         
39406         // is ancestor a text element.
39407         var ac =  range.commonAncestorContainer;
39408         if (ac.nodeType == 3) {
39409             ac = ac.parentNode;
39410         }
39411         
39412         var ar = ac.childNodes;
39413          
39414         var nodes = [];
39415         var other_nodes = [];
39416         var has_other_nodes = false;
39417         for (var i=0;i<ar.length;i++) {
39418             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39419                 continue;
39420             }
39421             // fullly contained node.
39422             
39423             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39424                 nodes.push(ar[i]);
39425                 continue;
39426             }
39427             
39428             // probably selected..
39429             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39430                 other_nodes.push(ar[i]);
39431                 continue;
39432             }
39433             // outer..
39434             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39435                 continue;
39436             }
39437             
39438             
39439             has_other_nodes = true;
39440         }
39441         if (!nodes.length && other_nodes.length) {
39442             nodes= other_nodes;
39443         }
39444         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39445             return false;
39446         }
39447         
39448         return nodes[0];
39449     },
39450     createRange: function(sel)
39451     {
39452         // this has strange effects when using with 
39453         // top toolbar - not sure if it's a great idea.
39454         //this.editor.contentWindow.focus();
39455         if (typeof sel != "undefined") {
39456             try {
39457                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39458             } catch(e) {
39459                 return this.doc.createRange();
39460             }
39461         } else {
39462             return this.doc.createRange();
39463         }
39464     },
39465     getParentElement: function()
39466     {
39467         
39468         this.assignDocWin();
39469         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39470         
39471         var range = this.createRange(sel);
39472          
39473         try {
39474             var p = range.commonAncestorContainer;
39475             while (p.nodeType == 3) { // text node
39476                 p = p.parentNode;
39477             }
39478             return p;
39479         } catch (e) {
39480             return null;
39481         }
39482     
39483     },
39484     /***
39485      *
39486      * Range intersection.. the hard stuff...
39487      *  '-1' = before
39488      *  '0' = hits..
39489      *  '1' = after.
39490      *         [ -- selected range --- ]
39491      *   [fail]                        [fail]
39492      *
39493      *    basically..
39494      *      if end is before start or  hits it. fail.
39495      *      if start is after end or hits it fail.
39496      *
39497      *   if either hits (but other is outside. - then it's not 
39498      *   
39499      *    
39500      **/
39501     
39502     
39503     // @see http://www.thismuchiknow.co.uk/?p=64.
39504     rangeIntersectsNode : function(range, node)
39505     {
39506         var nodeRange = node.ownerDocument.createRange();
39507         try {
39508             nodeRange.selectNode(node);
39509         } catch (e) {
39510             nodeRange.selectNodeContents(node);
39511         }
39512     
39513         var rangeStartRange = range.cloneRange();
39514         rangeStartRange.collapse(true);
39515     
39516         var rangeEndRange = range.cloneRange();
39517         rangeEndRange.collapse(false);
39518     
39519         var nodeStartRange = nodeRange.cloneRange();
39520         nodeStartRange.collapse(true);
39521     
39522         var nodeEndRange = nodeRange.cloneRange();
39523         nodeEndRange.collapse(false);
39524     
39525         return rangeStartRange.compareBoundaryPoints(
39526                  Range.START_TO_START, nodeEndRange) == -1 &&
39527                rangeEndRange.compareBoundaryPoints(
39528                  Range.START_TO_START, nodeStartRange) == 1;
39529         
39530          
39531     },
39532     rangeCompareNode : function(range, node)
39533     {
39534         var nodeRange = node.ownerDocument.createRange();
39535         try {
39536             nodeRange.selectNode(node);
39537         } catch (e) {
39538             nodeRange.selectNodeContents(node);
39539         }
39540         
39541         
39542         range.collapse(true);
39543     
39544         nodeRange.collapse(true);
39545      
39546         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
39547         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
39548          
39549         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
39550         
39551         var nodeIsBefore   =  ss == 1;
39552         var nodeIsAfter    = ee == -1;
39553         
39554         if (nodeIsBefore && nodeIsAfter)
39555             return 0; // outer
39556         if (!nodeIsBefore && nodeIsAfter)
39557             return 1; //right trailed.
39558         
39559         if (nodeIsBefore && !nodeIsAfter)
39560             return 2;  // left trailed.
39561         // fully contined.
39562         return 3;
39563     },
39564
39565     // private? - in a new class?
39566     cleanUpPaste :  function()
39567     {
39568         // cleans up the whole document..
39569       //  console.log('cleanuppaste');
39570         this.cleanUpChildren(this.doc.body);
39571         
39572         
39573     },
39574     cleanUpChildren : function (n)
39575     {
39576         if (!n.childNodes.length) {
39577             return;
39578         }
39579         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39580            this.cleanUpChild(n.childNodes[i]);
39581         }
39582     },
39583     
39584     
39585         
39586     
39587     cleanUpChild : function (node)
39588     {
39589         //console.log(node);
39590         if (node.nodeName == "#text") {
39591             // clean up silly Windows -- stuff?
39592             return; 
39593         }
39594         if (node.nodeName == "#comment") {
39595             node.parentNode.removeChild(node);
39596             // clean up silly Windows -- stuff?
39597             return; 
39598         }
39599         
39600         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39601             // remove node.
39602             node.parentNode.removeChild(node);
39603             return;
39604             
39605         }
39606         if (Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1) {
39607             this.cleanUpChildren(node);
39608             // inserts everything just before this node...
39609             while (node.childNodes.length) {
39610                 var cn = node.childNodes[0];
39611                 node.removeChild(cn);
39612                 node.parentNode.insertBefore(cn, node);
39613             }
39614             node.parentNode.removeChild(node);
39615             return;
39616         }
39617         
39618         if (!node.attributes || !node.attributes.length) {
39619             this.cleanUpChildren(node);
39620             return;
39621         }
39622         
39623         function cleanAttr(n,v)
39624         {
39625             
39626             if (v.match(/^\./) || v.match(/^\//)) {
39627                 return;
39628             }
39629             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39630                 return;
39631             }
39632             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39633             node.removeAttribute(n);
39634             
39635         }
39636         
39637         function cleanStyle(n,v)
39638         {
39639             if (v.match(/expression/)) { //XSS?? should we even bother..
39640                 node.removeAttribute(n);
39641                 return;
39642             }
39643             
39644             
39645             var parts = v.split(/;/);
39646             Roo.each(parts, function(p) {
39647                 p = p.replace(/\s+/g,'');
39648                 if (!p.length) {
39649                     return true;
39650                 }
39651                 var l = p.split(':').shift().replace(/\s+/g,'');
39652                 
39653                 // only allow 'c whitelisted system attributes'
39654                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39655                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39656                     node.removeAttribute(n);
39657                     return false;
39658                 }
39659                 return true;
39660             });
39661             
39662             
39663         }
39664         
39665         
39666         for (var i = node.attributes.length-1; i > -1 ; i--) {
39667             var a = node.attributes[i];
39668             //console.log(a);
39669             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39670                 node.removeAttribute(a.name);
39671                 return;
39672             }
39673             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39674                 cleanAttr(a.name,a.value); // fixme..
39675                 return;
39676             }
39677             if (a.name == 'style') {
39678                 cleanStyle(a.name,a.value);
39679             }
39680             /// clean up MS crap..
39681             if (a.name == 'class') {
39682                 if (a.value.match(/^Mso/)) {
39683                     node.className = '';
39684                 }
39685             }
39686             
39687             // style cleanup!?
39688             // class cleanup?
39689             
39690         }
39691         
39692         
39693         this.cleanUpChildren(node);
39694         
39695         
39696     }
39697     
39698     
39699     // hide stuff that is not compatible
39700     /**
39701      * @event blur
39702      * @hide
39703      */
39704     /**
39705      * @event change
39706      * @hide
39707      */
39708     /**
39709      * @event focus
39710      * @hide
39711      */
39712     /**
39713      * @event specialkey
39714      * @hide
39715      */
39716     /**
39717      * @cfg {String} fieldClass @hide
39718      */
39719     /**
39720      * @cfg {String} focusClass @hide
39721      */
39722     /**
39723      * @cfg {String} autoCreate @hide
39724      */
39725     /**
39726      * @cfg {String} inputType @hide
39727      */
39728     /**
39729      * @cfg {String} invalidClass @hide
39730      */
39731     /**
39732      * @cfg {String} invalidText @hide
39733      */
39734     /**
39735      * @cfg {String} msgFx @hide
39736      */
39737     /**
39738      * @cfg {String} validateOnBlur @hide
39739      */
39740 });
39741
39742 Roo.form.HtmlEditor.white = [
39743         'area', 'br', 'img', 'input', 'hr', 'wbr',
39744         
39745        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39746        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39747        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39748        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39749        'table',   'ul',         'xmp', 
39750        
39751        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39752       'thead',   'tr', 
39753      
39754       'dir', 'menu', 'ol', 'ul', 'dl',
39755        
39756       'embed',  'object'
39757 ];
39758
39759
39760 Roo.form.HtmlEditor.black = [
39761     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39762         'applet', // 
39763         'base',   'basefont', 'bgsound', 'blink',  'body', 
39764         'frame',  'frameset', 'head',    'html',   'ilayer', 
39765         'iframe', 'layer',  'link',     'meta',    'object',   
39766         'script', 'style' ,'title',  'xml' // clean later..
39767 ];
39768 Roo.form.HtmlEditor.clean = [
39769     'script', 'style', 'title', 'xml'
39770 ];
39771 Roo.form.HtmlEditor.remove = [
39772     'font'
39773 ];
39774 // attributes..
39775
39776 Roo.form.HtmlEditor.ablack = [
39777     'on'
39778 ];
39779     
39780 Roo.form.HtmlEditor.aclean = [ 
39781     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39782 ];
39783
39784 // protocols..
39785 Roo.form.HtmlEditor.pwhite= [
39786         'http',  'https',  'mailto'
39787 ];
39788
39789 // white listed style attributes.
39790 Roo.form.HtmlEditor.cwhite= [
39791         'text-align',
39792         'font-size'
39793 ];
39794
39795 // <script type="text/javascript">
39796 /*
39797  * Based on
39798  * Ext JS Library 1.1.1
39799  * Copyright(c) 2006-2007, Ext JS, LLC.
39800  *  
39801  
39802  */
39803
39804 /**
39805  * @class Roo.form.HtmlEditorToolbar1
39806  * Basic Toolbar
39807  * 
39808  * Usage:
39809  *
39810  new Roo.form.HtmlEditor({
39811     ....
39812     toolbars : [
39813         new Roo.form.HtmlEditorToolbar1({
39814             disable : { fonts: 1 , format: 1, ..., ... , ...],
39815             btns : [ .... ]
39816         })
39817     }
39818      
39819  * 
39820  * @cfg {Object} disable List of elements to disable..
39821  * @cfg {Array} btns List of additional buttons.
39822  * 
39823  * 
39824  * NEEDS Extra CSS? 
39825  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39826  */
39827  
39828 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39829 {
39830     
39831     Roo.apply(this, config);
39832     
39833     // default disabled, based on 'good practice'..
39834     this.disable = this.disable || {};
39835     Roo.applyIf(this.disable, {
39836         fontSize : true,
39837         colors : true,
39838         specialElements : true
39839     });
39840     
39841     
39842     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39843     // dont call parent... till later.
39844 }
39845
39846 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39847     
39848     tb: false,
39849     
39850     rendered: false,
39851     
39852     editor : false,
39853     /**
39854      * @cfg {Object} disable  List of toolbar elements to disable
39855          
39856      */
39857     disable : false,
39858       /**
39859      * @cfg {Array} fontFamilies An array of available font families
39860      */
39861     fontFamilies : [
39862         'Arial',
39863         'Courier New',
39864         'Tahoma',
39865         'Times New Roman',
39866         'Verdana'
39867     ],
39868     
39869     specialChars : [
39870            "&#169;",
39871           "&#174;",     
39872           "&#8482;",    
39873           "&#163;" ,    
39874          // "&#8212;",    
39875           "&#8230;",    
39876           "&#247;" ,    
39877         //  "&#225;" ,     ?? a acute?
39878            "&#8364;"    , //Euro
39879        //   "&#8220;"    ,
39880         //  "&#8221;"    ,
39881         //  "&#8226;"    ,
39882           "&#176;"  //   , // degrees
39883
39884          // "&#233;"     , // e ecute
39885          // "&#250;"     , // u ecute?
39886     ],
39887     
39888     specialElements : [
39889         {
39890             text: "Insert Table",
39891             xtype: 'MenuItem',
39892             xns : Roo.Menu,
39893             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
39894                 
39895         },
39896         {    
39897             text: "Insert Image",
39898             xtype: 'MenuItem',
39899             xns : Roo.Menu,
39900             ihtml : '<img src="about:blank"/>'
39901             
39902         }
39903         
39904          
39905     ],
39906     
39907     
39908     inputElements : [ 
39909             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39910             "input:submit", "input:button", "select", "textarea", "label" ],
39911     formats : [
39912         ["p"] ,  
39913         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39914         ["pre"],[ "code"], 
39915         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39916     ],
39917      /**
39918      * @cfg {String} defaultFont default font to use.
39919      */
39920     defaultFont: 'tahoma',
39921    
39922     fontSelect : false,
39923     
39924     
39925     formatCombo : false,
39926     
39927     init : function(editor)
39928     {
39929         this.editor = editor;
39930         
39931         
39932         var fid = editor.frameId;
39933         var etb = this;
39934         function btn(id, toggle, handler){
39935             var xid = fid + '-'+ id ;
39936             return {
39937                 id : xid,
39938                 cmd : id,
39939                 cls : 'x-btn-icon x-edit-'+id,
39940                 enableToggle:toggle !== false,
39941                 scope: editor, // was editor...
39942                 handler:handler||editor.relayBtnCmd,
39943                 clickEvent:'mousedown',
39944                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39945                 tabIndex:-1
39946             };
39947         }
39948         
39949         
39950         
39951         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39952         this.tb = tb;
39953          // stop form submits
39954         tb.el.on('click', function(e){
39955             e.preventDefault(); // what does this do?
39956         });
39957
39958         if(!this.disable.font && !Roo.isSafari){
39959             /* why no safari for fonts
39960             editor.fontSelect = tb.el.createChild({
39961                 tag:'select',
39962                 tabIndex: -1,
39963                 cls:'x-font-select',
39964                 html: editor.createFontOptions()
39965             });
39966             editor.fontSelect.on('change', function(){
39967                 var font = editor.fontSelect.dom.value;
39968                 editor.relayCmd('fontname', font);
39969                 editor.deferFocus();
39970             }, editor);
39971             tb.add(
39972                 editor.fontSelect.dom,
39973                 '-'
39974             );
39975             */
39976         };
39977         if(!this.disable.formats){
39978             this.formatCombo = new Roo.form.ComboBox({
39979                 store: new Roo.data.SimpleStore({
39980                     id : 'tag',
39981                     fields: ['tag'],
39982                     data : this.formats // from states.js
39983                 }),
39984                 blockFocus : true,
39985                 //autoCreate : {tag: "div",  size: "20"},
39986                 displayField:'tag',
39987                 typeAhead: false,
39988                 mode: 'local',
39989                 editable : false,
39990                 triggerAction: 'all',
39991                 emptyText:'Add tag',
39992                 selectOnFocus:true,
39993                 width:135,
39994                 listeners : {
39995                     'select': function(c, r, i) {
39996                         editor.insertTag(r.get('tag'));
39997                         editor.focus();
39998                     }
39999                 }
40000
40001             });
40002             tb.addField(this.formatCombo);
40003             
40004         }
40005         
40006         if(!this.disable.format){
40007             tb.add(
40008                 btn('bold'),
40009                 btn('italic'),
40010                 btn('underline')
40011             );
40012         };
40013         if(!this.disable.fontSize){
40014             tb.add(
40015                 '-',
40016                 
40017                 
40018                 btn('increasefontsize', false, editor.adjustFont),
40019                 btn('decreasefontsize', false, editor.adjustFont)
40020             );
40021         };
40022         
40023         
40024         if(!this.disable.colors){
40025             tb.add(
40026                 '-', {
40027                     id:editor.frameId +'-forecolor',
40028                     cls:'x-btn-icon x-edit-forecolor',
40029                     clickEvent:'mousedown',
40030                     tooltip: this.buttonTips['forecolor'] || undefined,
40031                     tabIndex:-1,
40032                     menu : new Roo.menu.ColorMenu({
40033                         allowReselect: true,
40034                         focus: Roo.emptyFn,
40035                         value:'000000',
40036                         plain:true,
40037                         selectHandler: function(cp, color){
40038                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
40039                             editor.deferFocus();
40040                         },
40041                         scope: editor,
40042                         clickEvent:'mousedown'
40043                     })
40044                 }, {
40045                     id:editor.frameId +'backcolor',
40046                     cls:'x-btn-icon x-edit-backcolor',
40047                     clickEvent:'mousedown',
40048                     tooltip: this.buttonTips['backcolor'] || undefined,
40049                     tabIndex:-1,
40050                     menu : new Roo.menu.ColorMenu({
40051                         focus: Roo.emptyFn,
40052                         value:'FFFFFF',
40053                         plain:true,
40054                         allowReselect: true,
40055                         selectHandler: function(cp, color){
40056                             if(Roo.isGecko){
40057                                 editor.execCmd('useCSS', false);
40058                                 editor.execCmd('hilitecolor', color);
40059                                 editor.execCmd('useCSS', true);
40060                                 editor.deferFocus();
40061                             }else{
40062                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
40063                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
40064                                 editor.deferFocus();
40065                             }
40066                         },
40067                         scope:editor,
40068                         clickEvent:'mousedown'
40069                     })
40070                 }
40071             );
40072         };
40073         // now add all the items...
40074         
40075
40076         if(!this.disable.alignments){
40077             tb.add(
40078                 '-',
40079                 btn('justifyleft'),
40080                 btn('justifycenter'),
40081                 btn('justifyright')
40082             );
40083         };
40084
40085         //if(!Roo.isSafari){
40086             if(!this.disable.links){
40087                 tb.add(
40088                     '-',
40089                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
40090                 );
40091             };
40092
40093             if(!this.disable.lists){
40094                 tb.add(
40095                     '-',
40096                     btn('insertorderedlist'),
40097                     btn('insertunorderedlist')
40098                 );
40099             }
40100             if(!this.disable.sourceEdit){
40101                 tb.add(
40102                     '-',
40103                     btn('sourceedit', true, function(btn){
40104                         this.toggleSourceEdit(btn.pressed);
40105                     })
40106                 );
40107             }
40108         //}
40109         
40110         var smenu = { };
40111         // special menu.. - needs to be tidied up..
40112         if (!this.disable.special) {
40113             smenu = {
40114                 text: "&#169;",
40115                 cls: 'x-edit-none',
40116                 
40117                 menu : {
40118                     items : []
40119                 }
40120             };
40121             for (var i =0; i < this.specialChars.length; i++) {
40122                 smenu.menu.items.push({
40123                     
40124                     html: this.specialChars[i],
40125                     handler: function(a,b) {
40126                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
40127                         
40128                     },
40129                     tabIndex:-1
40130                 });
40131             }
40132             
40133             
40134             tb.add(smenu);
40135             
40136             
40137         }
40138          
40139         if (!this.disable.specialElements) {
40140             var semenu = {
40141                 text: "Other;",
40142                 cls: 'x-edit-none',
40143                 menu : {
40144                     items : []
40145                 }
40146             };
40147             for (var i =0; i < this.specialElements.length; i++) {
40148                 semenu.menu.items.push(
40149                     Roo.apply({ 
40150                         handler: function(a,b) {
40151                             editor.insertAtCursor(this.ihtml);
40152                         }
40153                     }, this.specialElements[i])
40154                 );
40155                     
40156             }
40157             
40158             tb.add(semenu);
40159             
40160             
40161         }
40162          
40163         
40164         if (this.btns) {
40165             for(var i =0; i< this.btns.length;i++) {
40166                 var b = this.btns[i];
40167                 b.cls =  'x-edit-none';
40168                 b.scope = editor;
40169                 tb.add(b);
40170             }
40171         
40172         }
40173         
40174         
40175         
40176         // disable everything...
40177         
40178         this.tb.items.each(function(item){
40179            if(item.id != editor.frameId+ '-sourceedit'){
40180                 item.disable();
40181             }
40182         });
40183         this.rendered = true;
40184         
40185         // the all the btns;
40186         editor.on('editorevent', this.updateToolbar, this);
40187         // other toolbars need to implement this..
40188         //editor.on('editmodechange', this.updateToolbar, this);
40189     },
40190     
40191     
40192     
40193     /**
40194      * Protected method that will not generally be called directly. It triggers
40195      * a toolbar update by reading the markup state of the current selection in the editor.
40196      */
40197     updateToolbar: function(){
40198
40199         if(!this.editor.activated){
40200             this.editor.onFirstFocus();
40201             return;
40202         }
40203
40204         var btns = this.tb.items.map, 
40205             doc = this.editor.doc,
40206             frameId = this.editor.frameId;
40207
40208         if(!this.disable.font && !Roo.isSafari){
40209             /*
40210             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
40211             if(name != this.fontSelect.dom.value){
40212                 this.fontSelect.dom.value = name;
40213             }
40214             */
40215         }
40216         if(!this.disable.format){
40217             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
40218             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
40219             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
40220         }
40221         if(!this.disable.alignments){
40222             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
40223             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
40224             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
40225         }
40226         if(!Roo.isSafari && !this.disable.lists){
40227             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
40228             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
40229         }
40230         
40231         var ans = this.editor.getAllAncestors();
40232         if (this.formatCombo) {
40233             
40234             
40235             var store = this.formatCombo.store;
40236             this.formatCombo.setValue("");
40237             for (var i =0; i < ans.length;i++) {
40238                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
40239                     // select it..
40240                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
40241                     break;
40242                 }
40243             }
40244         }
40245         
40246         
40247         
40248         // hides menus... - so this cant be on a menu...
40249         Roo.menu.MenuMgr.hideAll();
40250
40251         //this.editorsyncValue();
40252     },
40253    
40254     
40255     createFontOptions : function(){
40256         var buf = [], fs = this.fontFamilies, ff, lc;
40257         for(var i = 0, len = fs.length; i< len; i++){
40258             ff = fs[i];
40259             lc = ff.toLowerCase();
40260             buf.push(
40261                 '<option value="',lc,'" style="font-family:',ff,';"',
40262                     (this.defaultFont == lc ? ' selected="true">' : '>'),
40263                     ff,
40264                 '</option>'
40265             );
40266         }
40267         return buf.join('');
40268     },
40269     
40270     toggleSourceEdit : function(sourceEditMode){
40271         if(sourceEditMode === undefined){
40272             sourceEditMode = !this.sourceEditMode;
40273         }
40274         this.sourceEditMode = sourceEditMode === true;
40275         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
40276         // just toggle the button?
40277         if(btn.pressed !== this.editor.sourceEditMode){
40278             btn.toggle(this.editor.sourceEditMode);
40279             return;
40280         }
40281         
40282         if(this.sourceEditMode){
40283             this.tb.items.each(function(item){
40284                 if(item.cmd != 'sourceedit'){
40285                     item.disable();
40286                 }
40287             });
40288           
40289         }else{
40290             if(this.initialized){
40291                 this.tb.items.each(function(item){
40292                     item.enable();
40293                 });
40294             }
40295             
40296         }
40297         // tell the editor that it's been pressed..
40298         this.editor.toggleSourceEdit(sourceEditMode);
40299        
40300     },
40301      /**
40302      * Object collection of toolbar tooltips for the buttons in the editor. The key
40303      * is the command id associated with that button and the value is a valid QuickTips object.
40304      * For example:
40305 <pre><code>
40306 {
40307     bold : {
40308         title: 'Bold (Ctrl+B)',
40309         text: 'Make the selected text bold.',
40310         cls: 'x-html-editor-tip'
40311     },
40312     italic : {
40313         title: 'Italic (Ctrl+I)',
40314         text: 'Make the selected text italic.',
40315         cls: 'x-html-editor-tip'
40316     },
40317     ...
40318 </code></pre>
40319     * @type Object
40320      */
40321     buttonTips : {
40322         bold : {
40323             title: 'Bold (Ctrl+B)',
40324             text: 'Make the selected text bold.',
40325             cls: 'x-html-editor-tip'
40326         },
40327         italic : {
40328             title: 'Italic (Ctrl+I)',
40329             text: 'Make the selected text italic.',
40330             cls: 'x-html-editor-tip'
40331         },
40332         underline : {
40333             title: 'Underline (Ctrl+U)',
40334             text: 'Underline the selected text.',
40335             cls: 'x-html-editor-tip'
40336         },
40337         increasefontsize : {
40338             title: 'Grow Text',
40339             text: 'Increase the font size.',
40340             cls: 'x-html-editor-tip'
40341         },
40342         decreasefontsize : {
40343             title: 'Shrink Text',
40344             text: 'Decrease the font size.',
40345             cls: 'x-html-editor-tip'
40346         },
40347         backcolor : {
40348             title: 'Text Highlight Color',
40349             text: 'Change the background color of the selected text.',
40350             cls: 'x-html-editor-tip'
40351         },
40352         forecolor : {
40353             title: 'Font Color',
40354             text: 'Change the color of the selected text.',
40355             cls: 'x-html-editor-tip'
40356         },
40357         justifyleft : {
40358             title: 'Align Text Left',
40359             text: 'Align text to the left.',
40360             cls: 'x-html-editor-tip'
40361         },
40362         justifycenter : {
40363             title: 'Center Text',
40364             text: 'Center text in the editor.',
40365             cls: 'x-html-editor-tip'
40366         },
40367         justifyright : {
40368             title: 'Align Text Right',
40369             text: 'Align text to the right.',
40370             cls: 'x-html-editor-tip'
40371         },
40372         insertunorderedlist : {
40373             title: 'Bullet List',
40374             text: 'Start a bulleted list.',
40375             cls: 'x-html-editor-tip'
40376         },
40377         insertorderedlist : {
40378             title: 'Numbered List',
40379             text: 'Start a numbered list.',
40380             cls: 'x-html-editor-tip'
40381         },
40382         createlink : {
40383             title: 'Hyperlink',
40384             text: 'Make the selected text a hyperlink.',
40385             cls: 'x-html-editor-tip'
40386         },
40387         sourceedit : {
40388             title: 'Source Edit',
40389             text: 'Switch to source editing mode.',
40390             cls: 'x-html-editor-tip'
40391         }
40392     },
40393     // private
40394     onDestroy : function(){
40395         if(this.rendered){
40396             
40397             this.tb.items.each(function(item){
40398                 if(item.menu){
40399                     item.menu.removeAll();
40400                     if(item.menu.el){
40401                         item.menu.el.destroy();
40402                     }
40403                 }
40404                 item.destroy();
40405             });
40406              
40407         }
40408     },
40409     onFirstFocus: function() {
40410         this.tb.items.each(function(item){
40411            item.enable();
40412         });
40413     }
40414 });
40415
40416
40417
40418
40419 // <script type="text/javascript">
40420 /*
40421  * Based on
40422  * Ext JS Library 1.1.1
40423  * Copyright(c) 2006-2007, Ext JS, LLC.
40424  *  
40425  
40426  */
40427
40428  
40429 /**
40430  * @class Roo.form.HtmlEditor.ToolbarContext
40431  * Context Toolbar
40432  * 
40433  * Usage:
40434  *
40435  new Roo.form.HtmlEditor({
40436     ....
40437     toolbars : [
40438         { xtype: 'ToolbarStandard', styles : {} }
40439         { xtype: 'ToolbarContext', disable : {} }
40440     ]
40441 })
40442
40443      
40444  * 
40445  * @config : {Object} disable List of elements to disable.. (not done yet.)
40446  * @config : {Object} styles  Map of styles available.
40447  * 
40448  */
40449
40450 Roo.form.HtmlEditor.ToolbarContext = function(config)
40451 {
40452     
40453     Roo.apply(this, config);
40454     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
40455     // dont call parent... till later.
40456     this.styles = this.styles || {};
40457 }
40458 Roo.form.HtmlEditor.ToolbarContext.types = {
40459     'IMG' : {
40460         width : {
40461             title: "Width",
40462             width: 40
40463         },
40464         height:  {
40465             title: "Height",
40466             width: 40
40467         },
40468         align: {
40469             title: "Align",
40470             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
40471             width : 80
40472             
40473         },
40474         border: {
40475             title: "Border",
40476             width: 40
40477         },
40478         alt: {
40479             title: "Alt",
40480             width: 120
40481         },
40482         src : {
40483             title: "Src",
40484             width: 220
40485         }
40486         
40487     },
40488     'A' : {
40489         name : {
40490             title: "Name",
40491             width: 50
40492         },
40493         href:  {
40494             title: "Href",
40495             width: 220
40496         } // border?
40497         
40498     },
40499     'TABLE' : {
40500         rows : {
40501             title: "Rows",
40502             width: 20
40503         },
40504         cols : {
40505             title: "Cols",
40506             width: 20
40507         },
40508         width : {
40509             title: "Width",
40510             width: 40
40511         },
40512         height : {
40513             title: "Height",
40514             width: 40
40515         },
40516         border : {
40517             title: "Border",
40518             width: 20
40519         }
40520     },
40521     'TD' : {
40522         width : {
40523             title: "Width",
40524             width: 40
40525         },
40526         height : {
40527             title: "Height",
40528             width: 40
40529         },   
40530         align: {
40531             title: "Align",
40532             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40533             width: 80
40534         },
40535         valign: {
40536             title: "Valign",
40537             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40538             width: 80
40539         },
40540         colspan: {
40541             title: "Colspan",
40542             width: 20
40543             
40544         }
40545     },
40546     'INPUT' : {
40547         name : {
40548             title: "name",
40549             width: 120
40550         },
40551         value : {
40552             title: "Value",
40553             width: 120
40554         },
40555         width : {
40556             title: "Width",
40557             width: 40
40558         }
40559     },
40560     'LABEL' : {
40561         'for' : {
40562             title: "For",
40563             width: 120
40564         }
40565     },
40566     'TEXTAREA' : {
40567           name : {
40568             title: "name",
40569             width: 120
40570         },
40571         rows : {
40572             title: "Rows",
40573             width: 20
40574         },
40575         cols : {
40576             title: "Cols",
40577             width: 20
40578         }
40579     },
40580     'SELECT' : {
40581         name : {
40582             title: "name",
40583             width: 120
40584         },
40585         selectoptions : {
40586             title: "Options",
40587             width: 200
40588         }
40589     },
40590     
40591     // should we really allow this??
40592     // should this just be 
40593     'BODY' : {
40594         title : {
40595             title: "title",
40596             width: 200,
40597             disabled : true
40598         }
40599     },
40600     '*' : {
40601         // empty..
40602     }
40603 };
40604
40605
40606
40607 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40608     
40609     tb: false,
40610     
40611     rendered: false,
40612     
40613     editor : false,
40614     /**
40615      * @cfg {Object} disable  List of toolbar elements to disable
40616          
40617      */
40618     disable : false,
40619     /**
40620      * @cfg {Object} styles List of styles 
40621      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
40622      *
40623      * These must be defined in the page, so they get rendered correctly..
40624      * .headline { }
40625      * TD.underline { }
40626      * 
40627      */
40628     styles : false,
40629     
40630     
40631     
40632     toolbars : false,
40633     
40634     init : function(editor)
40635     {
40636         this.editor = editor;
40637         
40638         
40639         var fid = editor.frameId;
40640         var etb = this;
40641         function btn(id, toggle, handler){
40642             var xid = fid + '-'+ id ;
40643             return {
40644                 id : xid,
40645                 cmd : id,
40646                 cls : 'x-btn-icon x-edit-'+id,
40647                 enableToggle:toggle !== false,
40648                 scope: editor, // was editor...
40649                 handler:handler||editor.relayBtnCmd,
40650                 clickEvent:'mousedown',
40651                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40652                 tabIndex:-1
40653             };
40654         }
40655         // create a new element.
40656         var wdiv = editor.wrap.createChild({
40657                 tag: 'div'
40658             }, editor.wrap.dom.firstChild.nextSibling, true);
40659         
40660         // can we do this more than once??
40661         
40662          // stop form submits
40663       
40664  
40665         // disable everything...
40666         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40667         this.toolbars = {};
40668            
40669         for (var i in  ty) {
40670           
40671             this.toolbars[i] = this.buildToolbar(ty[i],i);
40672         }
40673         this.tb = this.toolbars.BODY;
40674         this.tb.el.show();
40675         this.buildFooter();
40676         this.footer.show();
40677          
40678         this.rendered = true;
40679         
40680         // the all the btns;
40681         editor.on('editorevent', this.updateToolbar, this);
40682         // other toolbars need to implement this..
40683         //editor.on('editmodechange', this.updateToolbar, this);
40684     },
40685     
40686     
40687     
40688     /**
40689      * Protected method that will not generally be called directly. It triggers
40690      * a toolbar update by reading the markup state of the current selection in the editor.
40691      */
40692     updateToolbar: function(ignore_a,ignore_b,sel){
40693
40694         
40695         if(!this.editor.activated){
40696              this.editor.onFirstFocus();
40697             return;
40698         }
40699         var updateFooter = sel ? false : true;
40700         
40701         
40702         var ans = this.editor.getAllAncestors();
40703         
40704         // pick
40705         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40706         
40707         if (!sel) { 
40708             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40709             sel = sel ? sel : this.editor.doc.body;
40710             sel = sel.tagName.length ? sel : this.editor.doc.body;
40711             
40712         }
40713         // pick a menu that exists..
40714         var tn = sel.tagName.toUpperCase();
40715         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40716         
40717         tn = sel.tagName.toUpperCase();
40718         
40719         var lastSel = this.tb.selectedNode
40720         
40721         this.tb.selectedNode = sel;
40722         
40723         // if current menu does not match..
40724         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
40725                 
40726             this.tb.el.hide();
40727             ///console.log("show: " + tn);
40728             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
40729             this.tb.el.show();
40730             // update name
40731             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
40732             
40733             
40734             // update attributes
40735             if (this.tb.fields) {
40736                 this.tb.fields.each(function(e) {
40737                    e.setValue(sel.getAttribute(e.name));
40738                 });
40739             }
40740             
40741             // update styles
40742             var st = this.tb.fields.item(0);
40743             st.store.removeAll();
40744             var cn = sel.className.split(/\s+/);
40745             
40746             var avs = [];
40747             if (this.styles['*']) {
40748                 
40749                 Roo.each(this.styles['*'], function(v) {
40750                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40751                 });
40752             }
40753             if (this.styles[tn]) { 
40754                 Roo.each(this.styles[tn], function(v) {
40755                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
40756                 });
40757             }
40758             
40759             st.store.loadData(avs);
40760             st.collapse();
40761             st.setValue(cn);
40762             
40763             // flag our selected Node.
40764             this.tb.selectedNode = sel;
40765            
40766            
40767             Roo.menu.MenuMgr.hideAll();
40768
40769         }
40770         
40771         if (!updateFooter) {
40772             return;
40773         }
40774         // update the footer
40775         //
40776         var html = '';
40777         
40778         this.footerEls = ans.reverse();
40779         Roo.each(this.footerEls, function(a,i) {
40780             if (!a) { return; }
40781             html += html.length ? ' &gt; '  :  '';
40782             
40783             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
40784             
40785         });
40786        
40787         // 
40788         var sz = this.footDisp.up('td').getSize();
40789         this.footDisp.dom.style.width = (sz.width -10) + 'px';
40790         this.footDisp.dom.style.marginLeft = '5px';
40791         
40792         this.footDisp.dom.style.overflow = 'hidden';
40793         
40794         this.footDisp.dom.innerHTML = html;
40795             
40796         //this.editorsyncValue();
40797     },
40798    
40799        
40800     // private
40801     onDestroy : function(){
40802         if(this.rendered){
40803             
40804             this.tb.items.each(function(item){
40805                 if(item.menu){
40806                     item.menu.removeAll();
40807                     if(item.menu.el){
40808                         item.menu.el.destroy();
40809                     }
40810                 }
40811                 item.destroy();
40812             });
40813              
40814         }
40815     },
40816     onFirstFocus: function() {
40817         // need to do this for all the toolbars..
40818         this.tb.items.each(function(item){
40819            item.enable();
40820         });
40821     },
40822     buildToolbar: function(tlist, nm)
40823     {
40824         var editor = this.editor;
40825          // create a new element.
40826         var wdiv = editor.wrap.createChild({
40827                 tag: 'div'
40828             }, editor.wrap.dom.firstChild.nextSibling, true);
40829         
40830        
40831         var tb = new Roo.Toolbar(wdiv);
40832         // add the name..
40833         
40834         tb.add(nm+ ":&nbsp;");
40835         
40836         // styles...
40837         if (this.styles) {
40838             
40839             // this needs a multi-select checkbox...
40840             tb.addField( new Roo.form.ComboBox({
40841                 store: new Roo.data.SimpleStore({
40842                     id : 'val',
40843                     fields: ['val', 'selected'],
40844                     data : [] 
40845                 }),
40846                 name : 'className',
40847                 displayField:'val',
40848                 typeAhead: false,
40849                 mode: 'local',
40850                 editable : false,
40851                 triggerAction: 'all',
40852                 emptyText:'Select Style',
40853                 selectOnFocus:true,
40854                 width: 130,
40855                 listeners : {
40856                     'select': function(c, r, i) {
40857                         // initial support only for on class per el..
40858                         tb.selectedNode.className =  r ? r.get('val') : '';
40859                     }
40860                 }
40861     
40862             }));
40863         }
40864             
40865         
40866         
40867         for (var i in tlist) {
40868             
40869             var item = tlist[i];
40870             tb.add(item.title + ":&nbsp;");
40871             
40872             
40873             
40874             
40875             if (item.opts) {
40876                 // opts == pulldown..
40877                 tb.addField( new Roo.form.ComboBox({
40878                     store: new Roo.data.SimpleStore({
40879                         id : 'val',
40880                         fields: ['val'],
40881                         data : item.opts  
40882                     }),
40883                     name : i,
40884                     displayField:'val',
40885                     typeAhead: false,
40886                     mode: 'local',
40887                     editable : false,
40888                     triggerAction: 'all',
40889                     emptyText:'Select',
40890                     selectOnFocus:true,
40891                     width: item.width ? item.width  : 130,
40892                     listeners : {
40893                         'select': function(c, r, i) {
40894                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40895                         }
40896                     }
40897
40898                 }));
40899                 continue;
40900                     
40901                  
40902                 
40903                 tb.addField( new Roo.form.TextField({
40904                     name: i,
40905                     width: 100,
40906                     //allowBlank:false,
40907                     value: ''
40908                 }));
40909                 continue;
40910             }
40911             tb.addField( new Roo.form.TextField({
40912                 name: i,
40913                 width: item.width,
40914                 //allowBlank:true,
40915                 value: '',
40916                 listeners: {
40917                     'change' : function(f, nv, ov) {
40918                         tb.selectedNode.setAttribute(f.name, nv);
40919                     }
40920                 }
40921             }));
40922              
40923         }
40924         tb.el.on('click', function(e){
40925             e.preventDefault(); // what does this do?
40926         });
40927         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40928         tb.el.hide();
40929         tb.name = nm;
40930         // dont need to disable them... as they will get hidden
40931         return tb;
40932          
40933         
40934     },
40935     buildFooter : function()
40936     {
40937         
40938         var fel = this.editor.wrap.createChild();
40939         this.footer = new Roo.Toolbar(fel);
40940         // toolbar has scrolly on left / right?
40941         var footDisp= new Roo.Toolbar.Fill();
40942         var _t = this;
40943         this.footer.add(
40944             {
40945                 text : '&lt;',
40946                 xtype: 'Button',
40947                 handler : function() {
40948                     _t.footDisp.scrollTo('left',0,true)
40949                 }
40950             }
40951         );
40952         this.footer.add( footDisp );
40953         this.footer.add( 
40954             {
40955                 text : '&gt;',
40956                 xtype: 'Button',
40957                 handler : function() {
40958                     // no animation..
40959                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
40960                 }
40961             }
40962         );
40963         var fel = Roo.get(footDisp.el);
40964         fel.addClass('x-editor-context');
40965         this.footDispWrap = fel; 
40966         this.footDispWrap.overflow  = 'hidden';
40967         
40968         this.footDisp = fel.createChild();
40969         this.footDispWrap.on('click', this.onContextClick, this)
40970         
40971         
40972     },
40973     onContextClick : function (ev,dom)
40974     {
40975         ev.preventDefault();
40976         var  cn = dom.className;
40977         Roo.log(cn);
40978         if (!cn.match(/x-ed-loc-/)) {
40979             return;
40980         }
40981         var n = cn.split('-').pop();
40982         var ans = this.footerEls;
40983         var sel = ans[n];
40984         
40985          // pick
40986         var range = this.editor.createRange();
40987         
40988         range.selectNodeContents(sel);
40989         //range.selectNode(sel);
40990         
40991         
40992         var selection = this.editor.getSelection();
40993         selection.removeAllRanges();
40994         selection.addRange(range);
40995         
40996         
40997         
40998         this.updateToolbar(null, null, sel);
40999         
41000         
41001     }
41002     
41003     
41004     
41005     
41006     
41007 });
41008
41009
41010
41011
41012
41013 /*
41014  * Based on:
41015  * Ext JS Library 1.1.1
41016  * Copyright(c) 2006-2007, Ext JS, LLC.
41017  *
41018  * Originally Released Under LGPL - original licence link has changed is not relivant.
41019  *
41020  * Fork - LGPL
41021  * <script type="text/javascript">
41022  */
41023  
41024 /**
41025  * @class Roo.form.BasicForm
41026  * @extends Roo.util.Observable
41027  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
41028  * @constructor
41029  * @param {String/HTMLElement/Roo.Element} el The form element or its id
41030  * @param {Object} config Configuration options
41031  */
41032 Roo.form.BasicForm = function(el, config){
41033     this.allItems = [];
41034     this.childForms = [];
41035     Roo.apply(this, config);
41036     /*
41037      * The Roo.form.Field items in this form.
41038      * @type MixedCollection
41039      */
41040      
41041      
41042     this.items = new Roo.util.MixedCollection(false, function(o){
41043         return o.id || (o.id = Roo.id());
41044     });
41045     this.addEvents({
41046         /**
41047          * @event beforeaction
41048          * Fires before any action is performed. Return false to cancel the action.
41049          * @param {Form} this
41050          * @param {Action} action The action to be performed
41051          */
41052         beforeaction: true,
41053         /**
41054          * @event actionfailed
41055          * Fires when an action fails.
41056          * @param {Form} this
41057          * @param {Action} action The action that failed
41058          */
41059         actionfailed : true,
41060         /**
41061          * @event actioncomplete
41062          * Fires when an action is completed.
41063          * @param {Form} this
41064          * @param {Action} action The action that completed
41065          */
41066         actioncomplete : true
41067     });
41068     if(el){
41069         this.initEl(el);
41070     }
41071     Roo.form.BasicForm.superclass.constructor.call(this);
41072 };
41073
41074 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
41075     /**
41076      * @cfg {String} method
41077      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
41078      */
41079     /**
41080      * @cfg {DataReader} reader
41081      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
41082      * This is optional as there is built-in support for processing JSON.
41083      */
41084     /**
41085      * @cfg {DataReader} errorReader
41086      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
41087      * This is completely optional as there is built-in support for processing JSON.
41088      */
41089     /**
41090      * @cfg {String} url
41091      * The URL to use for form actions if one isn't supplied in the action options.
41092      */
41093     /**
41094      * @cfg {Boolean} fileUpload
41095      * Set to true if this form is a file upload.
41096      */
41097      
41098     /**
41099      * @cfg {Object} baseParams
41100      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
41101      */
41102      /**
41103      
41104     /**
41105      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
41106      */
41107     timeout: 30,
41108
41109     // private
41110     activeAction : null,
41111
41112     /**
41113      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
41114      * or setValues() data instead of when the form was first created.
41115      */
41116     trackResetOnLoad : false,
41117     
41118     
41119     /**
41120      * childForms - used for multi-tab forms
41121      * @type {Array}
41122      */
41123     childForms : false,
41124     
41125     /**
41126      * allItems - full list of fields.
41127      * @type {Array}
41128      */
41129     allItems : false,
41130     
41131     /**
41132      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
41133      * element by passing it or its id or mask the form itself by passing in true.
41134      * @type Mixed
41135      */
41136     waitMsgTarget : false,
41137
41138     // private
41139     initEl : function(el){
41140         this.el = Roo.get(el);
41141         this.id = this.el.id || Roo.id();
41142         this.el.on('submit', this.onSubmit, this);
41143         this.el.addClass('x-form');
41144     },
41145
41146     // private
41147     onSubmit : function(e){
41148         e.stopEvent();
41149     },
41150
41151     /**
41152      * Returns true if client-side validation on the form is successful.
41153      * @return Boolean
41154      */
41155     isValid : function(){
41156         var valid = true;
41157         this.items.each(function(f){
41158            if(!f.validate()){
41159                valid = false;
41160            }
41161         });
41162         return valid;
41163     },
41164
41165     /**
41166      * Returns true if any fields in this form have changed since their original load.
41167      * @return Boolean
41168      */
41169     isDirty : function(){
41170         var dirty = false;
41171         this.items.each(function(f){
41172            if(f.isDirty()){
41173                dirty = true;
41174                return false;
41175            }
41176         });
41177         return dirty;
41178     },
41179
41180     /**
41181      * Performs a predefined action (submit or load) or custom actions you define on this form.
41182      * @param {String} actionName The name of the action type
41183      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
41184      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
41185      * accept other config options):
41186      * <pre>
41187 Property          Type             Description
41188 ----------------  ---------------  ----------------------------------------------------------------------------------
41189 url               String           The url for the action (defaults to the form's url)
41190 method            String           The form method to use (defaults to the form's method, or POST if not defined)
41191 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
41192 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
41193                                    validate the form on the client (defaults to false)
41194      * </pre>
41195      * @return {BasicForm} this
41196      */
41197     doAction : function(action, options){
41198         if(typeof action == 'string'){
41199             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
41200         }
41201         if(this.fireEvent('beforeaction', this, action) !== false){
41202             this.beforeAction(action);
41203             action.run.defer(100, action);
41204         }
41205         return this;
41206     },
41207
41208     /**
41209      * Shortcut to do a submit action.
41210      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41211      * @return {BasicForm} this
41212      */
41213     submit : function(options){
41214         this.doAction('submit', options);
41215         return this;
41216     },
41217
41218     /**
41219      * Shortcut to do a load action.
41220      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
41221      * @return {BasicForm} this
41222      */
41223     load : function(options){
41224         this.doAction('load', options);
41225         return this;
41226     },
41227
41228     /**
41229      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
41230      * @param {Record} record The record to edit
41231      * @return {BasicForm} this
41232      */
41233     updateRecord : function(record){
41234         record.beginEdit();
41235         var fs = record.fields;
41236         fs.each(function(f){
41237             var field = this.findField(f.name);
41238             if(field){
41239                 record.set(f.name, field.getValue());
41240             }
41241         }, this);
41242         record.endEdit();
41243         return this;
41244     },
41245
41246     /**
41247      * Loads an Roo.data.Record into this form.
41248      * @param {Record} record The record to load
41249      * @return {BasicForm} this
41250      */
41251     loadRecord : function(record){
41252         this.setValues(record.data);
41253         return this;
41254     },
41255
41256     // private
41257     beforeAction : function(action){
41258         var o = action.options;
41259         
41260        
41261         if(this.waitMsgTarget === true){
41262             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
41263         }else if(this.waitMsgTarget){
41264             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
41265             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
41266         }else {
41267             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
41268         }
41269          
41270     },
41271
41272     // private
41273     afterAction : function(action, success){
41274         this.activeAction = null;
41275         var o = action.options;
41276         
41277         if(this.waitMsgTarget === true){
41278             this.el.unmask();
41279         }else if(this.waitMsgTarget){
41280             this.waitMsgTarget.unmask();
41281         }else{
41282             Roo.MessageBox.updateProgress(1);
41283             Roo.MessageBox.hide();
41284         }
41285          
41286         if(success){
41287             if(o.reset){
41288                 this.reset();
41289             }
41290             Roo.callback(o.success, o.scope, [this, action]);
41291             this.fireEvent('actioncomplete', this, action);
41292             
41293         }else{
41294             Roo.callback(o.failure, o.scope, [this, action]);
41295             // show an error message if no failed handler is set..
41296             if (!this.hasListener('actionfailed')) {
41297                 Roo.MessageBox.alert("Error", "Saving Failed, please check your entries");
41298             }
41299             
41300             this.fireEvent('actionfailed', this, action);
41301         }
41302         
41303     },
41304
41305     /**
41306      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
41307      * @param {String} id The value to search for
41308      * @return Field
41309      */
41310     findField : function(id){
41311         var field = this.items.get(id);
41312         if(!field){
41313             this.items.each(function(f){
41314                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
41315                     field = f;
41316                     return false;
41317                 }
41318             });
41319         }
41320         return field || null;
41321     },
41322
41323     /**
41324      * Add a secondary form to this one, 
41325      * Used to provide tabbed forms. One form is primary, with hidden values 
41326      * which mirror the elements from the other forms.
41327      * 
41328      * @param {Roo.form.Form} form to add.
41329      * 
41330      */
41331     addForm : function(form)
41332     {
41333        
41334         if (this.childForms.indexOf(form) > -1) {
41335             // already added..
41336             return;
41337         }
41338         this.childForms.push(form);
41339         var n = '';
41340         Roo.each(form.allItems, function (fe) {
41341             
41342             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
41343             if (this.findField(n)) { // already added..
41344                 return;
41345             }
41346             var add = new Roo.form.Hidden({
41347                 name : n
41348             });
41349             add.render(this.el);
41350             
41351             this.add( add );
41352         }, this);
41353         
41354     },
41355     /**
41356      * Mark fields in this form invalid in bulk.
41357      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
41358      * @return {BasicForm} this
41359      */
41360     markInvalid : function(errors){
41361         if(errors instanceof Array){
41362             for(var i = 0, len = errors.length; i < len; i++){
41363                 var fieldError = errors[i];
41364                 var f = this.findField(fieldError.id);
41365                 if(f){
41366                     f.markInvalid(fieldError.msg);
41367                 }
41368             }
41369         }else{
41370             var field, id;
41371             for(id in errors){
41372                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
41373                     field.markInvalid(errors[id]);
41374                 }
41375             }
41376         }
41377         Roo.each(this.childForms || [], function (f) {
41378             f.markInvalid(errors);
41379         });
41380         
41381         return this;
41382     },
41383
41384     /**
41385      * Set values for fields in this form in bulk.
41386      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
41387      * @return {BasicForm} this
41388      */
41389     setValues : function(values){
41390         if(values instanceof Array){ // array of objects
41391             for(var i = 0, len = values.length; i < len; i++){
41392                 var v = values[i];
41393                 var f = this.findField(v.id);
41394                 if(f){
41395                     f.setValue(v.value);
41396                     if(this.trackResetOnLoad){
41397                         f.originalValue = f.getValue();
41398                     }
41399                 }
41400             }
41401         }else{ // object hash
41402             var field, id;
41403             for(id in values){
41404                 if(typeof values[id] != 'function' && (field = this.findField(id))){
41405                     
41406                     if (field.setFromData && 
41407                         field.valueField && 
41408                         field.displayField &&
41409                         // combos' with local stores can 
41410                         // be queried via setValue()
41411                         // to set their value..
41412                         (field.store && !field.store.isLocal)
41413                         ) {
41414                         // it's a combo
41415                         var sd = { };
41416                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
41417                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
41418                         field.setFromData(sd);
41419                         
41420                     } else {
41421                         field.setValue(values[id]);
41422                     }
41423                     
41424                     
41425                     if(this.trackResetOnLoad){
41426                         field.originalValue = field.getValue();
41427                     }
41428                 }
41429             }
41430         }
41431          
41432         Roo.each(this.childForms || [], function (f) {
41433             f.setValues(values);
41434         });
41435                 
41436         return this;
41437     },
41438
41439     /**
41440      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
41441      * they are returned as an array.
41442      * @param {Boolean} asString
41443      * @return {Object}
41444      */
41445     getValues : function(asString){
41446         if (this.childForms) {
41447             // copy values from the child forms
41448             Roo.each(this.childForms, function (f) {
41449                 this.setValues(f.getValues());
41450             }, this);
41451         }
41452         
41453         
41454         
41455         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
41456         if(asString === true){
41457             return fs;
41458         }
41459         return Roo.urlDecode(fs);
41460     },
41461     
41462     /**
41463      * Returns the fields in this form as an object with key/value pairs. 
41464      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
41465      * @return {Object}
41466      */
41467     getFieldValues : function(with_hidden)
41468     {
41469         if (this.childForms) {
41470             // copy values from the child forms
41471             // should this call getFieldValues - probably not as we do not currently copy
41472             // hidden fields when we generate..
41473             Roo.each(this.childForms, function (f) {
41474                 this.setValues(f.getValues());
41475             }, this);
41476         }
41477         
41478         var ret = {};
41479         this.items.each(function(f){
41480             if (!f.getName()) {
41481                 return;
41482             }
41483             var v = f.getValue();
41484             // not sure if this supported any more..
41485             if ((typeof(v) == 'object') && f.getRawValue) {
41486                 v = f.getRawValue() ; // dates..
41487             }
41488             // combo boxes where name != hiddenName...
41489             if (f.name != f.getName()) {
41490                 ret[f.name] = f.getRawValue();
41491             }
41492             ret[f.getName()] = v;
41493         });
41494         
41495         return ret;
41496     },
41497
41498     /**
41499      * Clears all invalid messages in this form.
41500      * @return {BasicForm} this
41501      */
41502     clearInvalid : function(){
41503         this.items.each(function(f){
41504            f.clearInvalid();
41505         });
41506         
41507         Roo.each(this.childForms || [], function (f) {
41508             f.clearInvalid();
41509         });
41510         
41511         
41512         return this;
41513     },
41514
41515     /**
41516      * Resets this form.
41517      * @return {BasicForm} this
41518      */
41519     reset : function(){
41520         this.items.each(function(f){
41521             f.reset();
41522         });
41523         
41524         Roo.each(this.childForms || [], function (f) {
41525             f.reset();
41526         });
41527        
41528         
41529         return this;
41530     },
41531
41532     /**
41533      * Add Roo.form components to this form.
41534      * @param {Field} field1
41535      * @param {Field} field2 (optional)
41536      * @param {Field} etc (optional)
41537      * @return {BasicForm} this
41538      */
41539     add : function(){
41540         this.items.addAll(Array.prototype.slice.call(arguments, 0));
41541         return this;
41542     },
41543
41544
41545     /**
41546      * Removes a field from the items collection (does NOT remove its markup).
41547      * @param {Field} field
41548      * @return {BasicForm} this
41549      */
41550     remove : function(field){
41551         this.items.remove(field);
41552         return this;
41553     },
41554
41555     /**
41556      * Looks at the fields in this form, checks them for an id attribute,
41557      * and calls applyTo on the existing dom element with that id.
41558      * @return {BasicForm} this
41559      */
41560     render : function(){
41561         this.items.each(function(f){
41562             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
41563                 f.applyTo(f.id);
41564             }
41565         });
41566         return this;
41567     },
41568
41569     /**
41570      * Calls {@link Ext#apply} for all fields in this form with the passed object.
41571      * @param {Object} values
41572      * @return {BasicForm} this
41573      */
41574     applyToFields : function(o){
41575         this.items.each(function(f){
41576            Roo.apply(f, o);
41577         });
41578         return this;
41579     },
41580
41581     /**
41582      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
41583      * @param {Object} values
41584      * @return {BasicForm} this
41585      */
41586     applyIfToFields : function(o){
41587         this.items.each(function(f){
41588            Roo.applyIf(f, o);
41589         });
41590         return this;
41591     }
41592 });
41593
41594 // back compat
41595 Roo.BasicForm = Roo.form.BasicForm;/*
41596  * Based on:
41597  * Ext JS Library 1.1.1
41598  * Copyright(c) 2006-2007, Ext JS, LLC.
41599  *
41600  * Originally Released Under LGPL - original licence link has changed is not relivant.
41601  *
41602  * Fork - LGPL
41603  * <script type="text/javascript">
41604  */
41605
41606 /**
41607  * @class Roo.form.Form
41608  * @extends Roo.form.BasicForm
41609  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
41610  * @constructor
41611  * @param {Object} config Configuration options
41612  */
41613 Roo.form.Form = function(config){
41614     var xitems =  [];
41615     if (config.items) {
41616         xitems = config.items;
41617         delete config.items;
41618     }
41619    
41620     
41621     Roo.form.Form.superclass.constructor.call(this, null, config);
41622     this.url = this.url || this.action;
41623     if(!this.root){
41624         this.root = new Roo.form.Layout(Roo.applyIf({
41625             id: Roo.id()
41626         }, config));
41627     }
41628     this.active = this.root;
41629     /**
41630      * Array of all the buttons that have been added to this form via {@link addButton}
41631      * @type Array
41632      */
41633     this.buttons = [];
41634     this.allItems = [];
41635     this.addEvents({
41636         /**
41637          * @event clientvalidation
41638          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
41639          * @param {Form} this
41640          * @param {Boolean} valid true if the form has passed client-side validation
41641          */
41642         clientvalidation: true,
41643         /**
41644          * @event rendered
41645          * Fires when the form is rendered
41646          * @param {Roo.form.Form} form
41647          */
41648         rendered : true
41649     });
41650     
41651     if (this.progressUrl) {
41652             // push a hidden field onto the list of fields..
41653             this.addxtype( {
41654                     xns: Roo.form, 
41655                     xtype : 'Hidden', 
41656                     name : 'UPLOAD_IDENTIFIER' 
41657             });
41658         }
41659         
41660     
41661     Roo.each(xitems, this.addxtype, this);
41662     
41663     
41664     
41665 };
41666
41667 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
41668     /**
41669      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
41670      */
41671     /**
41672      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
41673      */
41674     /**
41675      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
41676      */
41677     buttonAlign:'center',
41678
41679     /**
41680      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
41681      */
41682     minButtonWidth:75,
41683
41684     /**
41685      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
41686      * This property cascades to child containers if not set.
41687      */
41688     labelAlign:'left',
41689
41690     /**
41691      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
41692      * fires a looping event with that state. This is required to bind buttons to the valid
41693      * state using the config value formBind:true on the button.
41694      */
41695     monitorValid : false,
41696
41697     /**
41698      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
41699      */
41700     monitorPoll : 200,
41701     
41702     /**
41703      * @cfg {String} progressUrl - Url to return progress data 
41704      */
41705     
41706     progressUrl : false,
41707   
41708     /**
41709      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
41710      * fields are added and the column is closed. If no fields are passed the column remains open
41711      * until end() is called.
41712      * @param {Object} config The config to pass to the column
41713      * @param {Field} field1 (optional)
41714      * @param {Field} field2 (optional)
41715      * @param {Field} etc (optional)
41716      * @return Column The column container object
41717      */
41718     column : function(c){
41719         var col = new Roo.form.Column(c);
41720         this.start(col);
41721         if(arguments.length > 1){ // duplicate code required because of Opera
41722             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41723             this.end();
41724         }
41725         return col;
41726     },
41727
41728     /**
41729      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
41730      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
41731      * until end() is called.
41732      * @param {Object} config The config to pass to the fieldset
41733      * @param {Field} field1 (optional)
41734      * @param {Field} field2 (optional)
41735      * @param {Field} etc (optional)
41736      * @return FieldSet The fieldset container object
41737      */
41738     fieldset : function(c){
41739         var fs = new Roo.form.FieldSet(c);
41740         this.start(fs);
41741         if(arguments.length > 1){ // duplicate code required because of Opera
41742             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41743             this.end();
41744         }
41745         return fs;
41746     },
41747
41748     /**
41749      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41750      * fields are added and the container is closed. If no fields are passed the container remains open
41751      * until end() is called.
41752      * @param {Object} config The config to pass to the Layout
41753      * @param {Field} field1 (optional)
41754      * @param {Field} field2 (optional)
41755      * @param {Field} etc (optional)
41756      * @return Layout The container object
41757      */
41758     container : function(c){
41759         var l = new Roo.form.Layout(c);
41760         this.start(l);
41761         if(arguments.length > 1){ // duplicate code required because of Opera
41762             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41763             this.end();
41764         }
41765         return l;
41766     },
41767
41768     /**
41769      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41770      * @param {Object} container A Roo.form.Layout or subclass of Layout
41771      * @return {Form} this
41772      */
41773     start : function(c){
41774         // cascade label info
41775         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41776         this.active.stack.push(c);
41777         c.ownerCt = this.active;
41778         this.active = c;
41779         return this;
41780     },
41781
41782     /**
41783      * Closes the current open container
41784      * @return {Form} this
41785      */
41786     end : function(){
41787         if(this.active == this.root){
41788             return this;
41789         }
41790         this.active = this.active.ownerCt;
41791         return this;
41792     },
41793
41794     /**
41795      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41796      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41797      * as the label of the field.
41798      * @param {Field} field1
41799      * @param {Field} field2 (optional)
41800      * @param {Field} etc. (optional)
41801      * @return {Form} this
41802      */
41803     add : function(){
41804         this.active.stack.push.apply(this.active.stack, arguments);
41805         this.allItems.push.apply(this.allItems,arguments);
41806         var r = [];
41807         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41808             if(a[i].isFormField){
41809                 r.push(a[i]);
41810             }
41811         }
41812         if(r.length > 0){
41813             Roo.form.Form.superclass.add.apply(this, r);
41814         }
41815         return this;
41816     },
41817     
41818
41819     
41820     
41821     
41822      /**
41823      * Find any element that has been added to a form, using it's ID or name
41824      * This can include framesets, columns etc. along with regular fields..
41825      * @param {String} id - id or name to find.
41826      
41827      * @return {Element} e - or false if nothing found.
41828      */
41829     findbyId : function(id)
41830     {
41831         var ret = false;
41832         if (!id) {
41833             return ret;
41834         }
41835         Roo.each(this.allItems, function(f){
41836             if (f.id == id || f.name == id ){
41837                 ret = f;
41838                 return false;
41839             }
41840         });
41841         return ret;
41842     },
41843
41844     
41845     
41846     /**
41847      * Render this form into the passed container. This should only be called once!
41848      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41849      * @return {Form} this
41850      */
41851     render : function(ct)
41852     {
41853         
41854         
41855         
41856         ct = Roo.get(ct);
41857         var o = this.autoCreate || {
41858             tag: 'form',
41859             method : this.method || 'POST',
41860             id : this.id || Roo.id()
41861         };
41862         this.initEl(ct.createChild(o));
41863
41864         this.root.render(this.el);
41865         
41866        
41867              
41868         this.items.each(function(f){
41869             f.render('x-form-el-'+f.id);
41870         });
41871
41872         if(this.buttons.length > 0){
41873             // tables are required to maintain order and for correct IE layout
41874             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41875                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41876                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41877             }}, null, true);
41878             var tr = tb.getElementsByTagName('tr')[0];
41879             for(var i = 0, len = this.buttons.length; i < len; i++) {
41880                 var b = this.buttons[i];
41881                 var td = document.createElement('td');
41882                 td.className = 'x-form-btn-td';
41883                 b.render(tr.appendChild(td));
41884             }
41885         }
41886         if(this.monitorValid){ // initialize after render
41887             this.startMonitoring();
41888         }
41889         this.fireEvent('rendered', this);
41890         return this;
41891     },
41892
41893     /**
41894      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41895      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41896      * object or a valid Roo.DomHelper element config
41897      * @param {Function} handler The function called when the button is clicked
41898      * @param {Object} scope (optional) The scope of the handler function
41899      * @return {Roo.Button}
41900      */
41901     addButton : function(config, handler, scope){
41902         var bc = {
41903             handler: handler,
41904             scope: scope,
41905             minWidth: this.minButtonWidth,
41906             hideParent:true
41907         };
41908         if(typeof config == "string"){
41909             bc.text = config;
41910         }else{
41911             Roo.apply(bc, config);
41912         }
41913         var btn = new Roo.Button(null, bc);
41914         this.buttons.push(btn);
41915         return btn;
41916     },
41917
41918      /**
41919      * Adds a series of form elements (using the xtype property as the factory method.
41920      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41921      * @param {Object} config 
41922      */
41923     
41924     addxtype : function()
41925     {
41926         var ar = Array.prototype.slice.call(arguments, 0);
41927         var ret = false;
41928         for(var i = 0; i < ar.length; i++) {
41929             if (!ar[i]) {
41930                 continue; // skip -- if this happends something invalid got sent, we 
41931                 // should ignore it, as basically that interface element will not show up
41932                 // and that should be pretty obvious!!
41933             }
41934             
41935             if (Roo.form[ar[i].xtype]) {
41936                 ar[i].form = this;
41937                 var fe = Roo.factory(ar[i], Roo.form);
41938                 if (!ret) {
41939                     ret = fe;
41940                 }
41941                 fe.form = this;
41942                 if (fe.store) {
41943                     fe.store.form = this;
41944                 }
41945                 if (fe.isLayout) {  
41946                          
41947                     this.start(fe);
41948                     this.allItems.push(fe);
41949                     if (fe.items && fe.addxtype) {
41950                         fe.addxtype.apply(fe, fe.items);
41951                         delete fe.items;
41952                     }
41953                      this.end();
41954                     continue;
41955                 }
41956                 
41957                 
41958                  
41959                 this.add(fe);
41960               //  console.log('adding ' + ar[i].xtype);
41961             }
41962             if (ar[i].xtype == 'Button') {  
41963                 //console.log('adding button');
41964                 //console.log(ar[i]);
41965                 this.addButton(ar[i]);
41966                 this.allItems.push(fe);
41967                 continue;
41968             }
41969             
41970             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41971                 alert('end is not supported on xtype any more, use items');
41972             //    this.end();
41973             //    //console.log('adding end');
41974             }
41975             
41976         }
41977         return ret;
41978     },
41979     
41980     /**
41981      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41982      * option "monitorValid"
41983      */
41984     startMonitoring : function(){
41985         if(!this.bound){
41986             this.bound = true;
41987             Roo.TaskMgr.start({
41988                 run : this.bindHandler,
41989                 interval : this.monitorPoll || 200,
41990                 scope: this
41991             });
41992         }
41993     },
41994
41995     /**
41996      * Stops monitoring of the valid state of this form
41997      */
41998     stopMonitoring : function(){
41999         this.bound = false;
42000     },
42001
42002     // private
42003     bindHandler : function(){
42004         if(!this.bound){
42005             return false; // stops binding
42006         }
42007         var valid = true;
42008         this.items.each(function(f){
42009             if(!f.isValid(true)){
42010                 valid = false;
42011                 return false;
42012             }
42013         });
42014         for(var i = 0, len = this.buttons.length; i < len; i++){
42015             var btn = this.buttons[i];
42016             if(btn.formBind === true && btn.disabled === valid){
42017                 btn.setDisabled(!valid);
42018             }
42019         }
42020         this.fireEvent('clientvalidation', this, valid);
42021     }
42022     
42023     
42024     
42025     
42026     
42027     
42028     
42029     
42030 });
42031
42032
42033 // back compat
42034 Roo.Form = Roo.form.Form;
42035 /*
42036  * Based on:
42037  * Ext JS Library 1.1.1
42038  * Copyright(c) 2006-2007, Ext JS, LLC.
42039  *
42040  * Originally Released Under LGPL - original licence link has changed is not relivant.
42041  *
42042  * Fork - LGPL
42043  * <script type="text/javascript">
42044  */
42045  
42046  /**
42047  * @class Roo.form.Action
42048  * Internal Class used to handle form actions
42049  * @constructor
42050  * @param {Roo.form.BasicForm} el The form element or its id
42051  * @param {Object} config Configuration options
42052  */
42053  
42054  
42055 // define the action interface
42056 Roo.form.Action = function(form, options){
42057     this.form = form;
42058     this.options = options || {};
42059 };
42060 /**
42061  * Client Validation Failed
42062  * @const 
42063  */
42064 Roo.form.Action.CLIENT_INVALID = 'client';
42065 /**
42066  * Server Validation Failed
42067  * @const 
42068  */
42069  Roo.form.Action.SERVER_INVALID = 'server';
42070  /**
42071  * Connect to Server Failed
42072  * @const 
42073  */
42074 Roo.form.Action.CONNECT_FAILURE = 'connect';
42075 /**
42076  * Reading Data from Server Failed
42077  * @const 
42078  */
42079 Roo.form.Action.LOAD_FAILURE = 'load';
42080
42081 Roo.form.Action.prototype = {
42082     type : 'default',
42083     failureType : undefined,
42084     response : undefined,
42085     result : undefined,
42086
42087     // interface method
42088     run : function(options){
42089
42090     },
42091
42092     // interface method
42093     success : function(response){
42094
42095     },
42096
42097     // interface method
42098     handleResponse : function(response){
42099
42100     },
42101
42102     // default connection failure
42103     failure : function(response){
42104         
42105         this.response = response;
42106         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42107         this.form.afterAction(this, false);
42108     },
42109
42110     processResponse : function(response){
42111         this.response = response;
42112         if(!response.responseText){
42113             return true;
42114         }
42115         this.result = this.handleResponse(response);
42116         return this.result;
42117     },
42118
42119     // utility functions used internally
42120     getUrl : function(appendParams){
42121         var url = this.options.url || this.form.url || this.form.el.dom.action;
42122         if(appendParams){
42123             var p = this.getParams();
42124             if(p){
42125                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
42126             }
42127         }
42128         return url;
42129     },
42130
42131     getMethod : function(){
42132         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
42133     },
42134
42135     getParams : function(){
42136         var bp = this.form.baseParams;
42137         var p = this.options.params;
42138         if(p){
42139             if(typeof p == "object"){
42140                 p = Roo.urlEncode(Roo.applyIf(p, bp));
42141             }else if(typeof p == 'string' && bp){
42142                 p += '&' + Roo.urlEncode(bp);
42143             }
42144         }else if(bp){
42145             p = Roo.urlEncode(bp);
42146         }
42147         return p;
42148     },
42149
42150     createCallback : function(){
42151         return {
42152             success: this.success,
42153             failure: this.failure,
42154             scope: this,
42155             timeout: (this.form.timeout*1000),
42156             upload: this.form.fileUpload ? this.success : undefined
42157         };
42158     }
42159 };
42160
42161 Roo.form.Action.Submit = function(form, options){
42162     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
42163 };
42164
42165 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
42166     type : 'submit',
42167
42168     haveProgress : false,
42169     uploadComplete : false,
42170     
42171     // uploadProgress indicator.
42172     uploadProgress : function()
42173     {
42174         if (!this.form.progressUrl) {
42175             return;
42176         }
42177         
42178         if (!this.haveProgress) {
42179             Roo.MessageBox.progress("Uploading", "Uploading");
42180         }
42181         if (this.uploadComplete) {
42182            Roo.MessageBox.hide();
42183            return;
42184         }
42185         
42186         this.haveProgress = true;
42187    
42188         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
42189         
42190         var c = new Roo.data.Connection();
42191         c.request({
42192             url : this.form.progressUrl,
42193             params: {
42194                 id : uid
42195             },
42196             method: 'GET',
42197             success : function(req){
42198                //console.log(data);
42199                 var rdata = false;
42200                 var edata;
42201                 try  {
42202                    rdata = Roo.decode(req.responseText)
42203                 } catch (e) {
42204                     Roo.log("Invalid data from server..");
42205                     Roo.log(edata);
42206                     return;
42207                 }
42208                 if (!rdata || !rdata.success) {
42209                     Roo.log(rdata);
42210                     return;
42211                 }
42212                 var data = rdata.data;
42213                 
42214                 if (this.uploadComplete) {
42215                    Roo.MessageBox.hide();
42216                    return;
42217                 }
42218                    
42219                 if (data){
42220                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
42221                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
42222                     );
42223                 }
42224                 this.uploadProgress.defer(2000,this);
42225             },
42226        
42227             failure: function(data) {
42228                 Roo.log('progress url failed ');
42229                 Roo.log(data);
42230             },
42231             scope : this
42232         });
42233            
42234     },
42235     
42236     
42237     run : function()
42238     {
42239         // run get Values on the form, so it syncs any secondary forms.
42240         this.form.getValues();
42241         
42242         var o = this.options;
42243         var method = this.getMethod();
42244         var isPost = method == 'POST';
42245         if(o.clientValidation === false || this.form.isValid()){
42246             
42247             if (this.form.progressUrl) {
42248                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
42249                     (new Date() * 1) + '' + Math.random());
42250                     
42251             } 
42252             
42253             
42254             Roo.Ajax.request(Roo.apply(this.createCallback(), {
42255                 form:this.form.el.dom,
42256                 url:this.getUrl(!isPost),
42257                 method: method,
42258                 params:isPost ? this.getParams() : null,
42259                 isUpload: this.form.fileUpload
42260             }));
42261             
42262             this.uploadProgress();
42263
42264         }else if (o.clientValidation !== false){ // client validation failed
42265             this.failureType = Roo.form.Action.CLIENT_INVALID;
42266             this.form.afterAction(this, false);
42267         }
42268     },
42269
42270     success : function(response)
42271     {
42272         this.uploadComplete= true;
42273         if (this.haveProgress) {
42274             Roo.MessageBox.hide();
42275         }
42276         
42277         
42278         var result = this.processResponse(response);
42279         if(result === true || result.success){
42280             this.form.afterAction(this, true);
42281             return;
42282         }
42283         if(result.errors){
42284             this.form.markInvalid(result.errors);
42285             this.failureType = Roo.form.Action.SERVER_INVALID;
42286         }
42287         this.form.afterAction(this, false);
42288     },
42289     failure : function(response)
42290     {
42291         this.uploadComplete= true;
42292         if (this.haveProgress) {
42293             Roo.MessageBox.hide();
42294         }
42295         
42296         
42297         this.response = response;
42298         this.failureType = Roo.form.Action.CONNECT_FAILURE;
42299         this.form.afterAction(this, false);
42300     },
42301     
42302     handleResponse : function(response){
42303         if(this.form.errorReader){
42304             var rs = this.form.errorReader.read(response);
42305             var errors = [];
42306             if(rs.records){
42307                 for(var i = 0, len = rs.records.length; i < len; i++) {
42308                     var r = rs.records[i];
42309                     errors[i] = r.data;
42310                 }
42311             }
42312             if(errors.length < 1){
42313                 errors = null;
42314             }
42315             return {
42316                 success : rs.success,
42317                 errors : errors
42318             };
42319         }
42320         var ret = false;
42321         try {
42322             ret = Roo.decode(response.responseText);
42323         } catch (e) {
42324             ret = {
42325                 success: false,
42326                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
42327                 errors : []
42328             };
42329         }
42330         return ret;
42331         
42332     }
42333 });
42334
42335
42336 Roo.form.Action.Load = function(form, options){
42337     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
42338     this.reader = this.form.reader;
42339 };
42340
42341 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
42342     type : 'load',
42343
42344     run : function(){
42345         
42346         Roo.Ajax.request(Roo.apply(
42347                 this.createCallback(), {
42348                     method:this.getMethod(),
42349                     url:this.getUrl(false),
42350                     params:this.getParams()
42351         }));
42352     },
42353
42354     success : function(response){
42355         
42356         var result = this.processResponse(response);
42357         if(result === true || !result.success || !result.data){
42358             this.failureType = Roo.form.Action.LOAD_FAILURE;
42359             this.form.afterAction(this, false);
42360             return;
42361         }
42362         this.form.clearInvalid();
42363         this.form.setValues(result.data);
42364         this.form.afterAction(this, true);
42365     },
42366
42367     handleResponse : function(response){
42368         if(this.form.reader){
42369             var rs = this.form.reader.read(response);
42370             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
42371             return {
42372                 success : rs.success,
42373                 data : data
42374             };
42375         }
42376         return Roo.decode(response.responseText);
42377     }
42378 });
42379
42380 Roo.form.Action.ACTION_TYPES = {
42381     'load' : Roo.form.Action.Load,
42382     'submit' : Roo.form.Action.Submit
42383 };/*
42384  * Based on:
42385  * Ext JS Library 1.1.1
42386  * Copyright(c) 2006-2007, Ext JS, LLC.
42387  *
42388  * Originally Released Under LGPL - original licence link has changed is not relivant.
42389  *
42390  * Fork - LGPL
42391  * <script type="text/javascript">
42392  */
42393  
42394 /**
42395  * @class Roo.form.Layout
42396  * @extends Roo.Component
42397  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
42398  * @constructor
42399  * @param {Object} config Configuration options
42400  */
42401 Roo.form.Layout = function(config){
42402     var xitems = [];
42403     if (config.items) {
42404         xitems = config.items;
42405         delete config.items;
42406     }
42407     Roo.form.Layout.superclass.constructor.call(this, config);
42408     this.stack = [];
42409     Roo.each(xitems, this.addxtype, this);
42410      
42411 };
42412
42413 Roo.extend(Roo.form.Layout, Roo.Component, {
42414     /**
42415      * @cfg {String/Object} autoCreate
42416      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
42417      */
42418     /**
42419      * @cfg {String/Object/Function} style
42420      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
42421      * a function which returns such a specification.
42422      */
42423     /**
42424      * @cfg {String} labelAlign
42425      * Valid values are "left," "top" and "right" (defaults to "left")
42426      */
42427     /**
42428      * @cfg {Number} labelWidth
42429      * Fixed width in pixels of all field labels (defaults to undefined)
42430      */
42431     /**
42432      * @cfg {Boolean} clear
42433      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
42434      */
42435     clear : true,
42436     /**
42437      * @cfg {String} labelSeparator
42438      * The separator to use after field labels (defaults to ':')
42439      */
42440     labelSeparator : ':',
42441     /**
42442      * @cfg {Boolean} hideLabels
42443      * True to suppress the display of field labels in this layout (defaults to false)
42444      */
42445     hideLabels : false,
42446
42447     // private
42448     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
42449     
42450     isLayout : true,
42451     
42452     // private
42453     onRender : function(ct, position){
42454         if(this.el){ // from markup
42455             this.el = Roo.get(this.el);
42456         }else {  // generate
42457             var cfg = this.getAutoCreate();
42458             this.el = ct.createChild(cfg, position);
42459         }
42460         if(this.style){
42461             this.el.applyStyles(this.style);
42462         }
42463         if(this.labelAlign){
42464             this.el.addClass('x-form-label-'+this.labelAlign);
42465         }
42466         if(this.hideLabels){
42467             this.labelStyle = "display:none";
42468             this.elementStyle = "padding-left:0;";
42469         }else{
42470             if(typeof this.labelWidth == 'number'){
42471                 this.labelStyle = "width:"+this.labelWidth+"px;";
42472                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
42473             }
42474             if(this.labelAlign == 'top'){
42475                 this.labelStyle = "width:auto;";
42476                 this.elementStyle = "padding-left:0;";
42477             }
42478         }
42479         var stack = this.stack;
42480         var slen = stack.length;
42481         if(slen > 0){
42482             if(!this.fieldTpl){
42483                 var t = new Roo.Template(
42484                     '<div class="x-form-item {5}">',
42485                         '<label for="{0}" style="{2}">{1}{4}</label>',
42486                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42487                         '</div>',
42488                     '</div><div class="x-form-clear-left"></div>'
42489                 );
42490                 t.disableFormats = true;
42491                 t.compile();
42492                 Roo.form.Layout.prototype.fieldTpl = t;
42493             }
42494             for(var i = 0; i < slen; i++) {
42495                 if(stack[i].isFormField){
42496                     this.renderField(stack[i]);
42497                 }else{
42498                     this.renderComponent(stack[i]);
42499                 }
42500             }
42501         }
42502         if(this.clear){
42503             this.el.createChild({cls:'x-form-clear'});
42504         }
42505     },
42506
42507     // private
42508     renderField : function(f){
42509         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
42510                f.id, //0
42511                f.fieldLabel, //1
42512                f.labelStyle||this.labelStyle||'', //2
42513                this.elementStyle||'', //3
42514                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
42515                f.itemCls||this.itemCls||''  //5
42516        ], true).getPrevSibling());
42517     },
42518
42519     // private
42520     renderComponent : function(c){
42521         c.render(c.isLayout ? this.el : this.el.createChild());    
42522     },
42523     /**
42524      * Adds a object form elements (using the xtype property as the factory method.)
42525      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
42526      * @param {Object} config 
42527      */
42528     addxtype : function(o)
42529     {
42530         // create the lement.
42531         o.form = this.form;
42532         var fe = Roo.factory(o, Roo.form);
42533         this.form.allItems.push(fe);
42534         this.stack.push(fe);
42535         
42536         if (fe.isFormField) {
42537             this.form.items.add(fe);
42538         }
42539          
42540         return fe;
42541     }
42542 });
42543
42544 /**
42545  * @class Roo.form.Column
42546  * @extends Roo.form.Layout
42547  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
42548  * @constructor
42549  * @param {Object} config Configuration options
42550  */
42551 Roo.form.Column = function(config){
42552     Roo.form.Column.superclass.constructor.call(this, config);
42553 };
42554
42555 Roo.extend(Roo.form.Column, Roo.form.Layout, {
42556     /**
42557      * @cfg {Number/String} width
42558      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42559      */
42560     /**
42561      * @cfg {String/Object} autoCreate
42562      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
42563      */
42564
42565     // private
42566     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
42567
42568     // private
42569     onRender : function(ct, position){
42570         Roo.form.Column.superclass.onRender.call(this, ct, position);
42571         if(this.width){
42572             this.el.setWidth(this.width);
42573         }
42574     }
42575 });
42576
42577
42578 /**
42579  * @class Roo.form.Row
42580  * @extends Roo.form.Layout
42581  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
42582  * @constructor
42583  * @param {Object} config Configuration options
42584  */
42585
42586  
42587 Roo.form.Row = function(config){
42588     Roo.form.Row.superclass.constructor.call(this, config);
42589 };
42590  
42591 Roo.extend(Roo.form.Row, Roo.form.Layout, {
42592       /**
42593      * @cfg {Number/String} width
42594      * The fixed width of the column in pixels or CSS value (defaults to "auto")
42595      */
42596     /**
42597      * @cfg {Number/String} height
42598      * The fixed height of the column in pixels or CSS value (defaults to "auto")
42599      */
42600     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
42601     
42602     padWidth : 20,
42603     // private
42604     onRender : function(ct, position){
42605         //console.log('row render');
42606         if(!this.rowTpl){
42607             var t = new Roo.Template(
42608                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
42609                     '<label for="{0}" style="{2}">{1}{4}</label>',
42610                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
42611                     '</div>',
42612                 '</div>'
42613             );
42614             t.disableFormats = true;
42615             t.compile();
42616             Roo.form.Layout.prototype.rowTpl = t;
42617         }
42618         this.fieldTpl = this.rowTpl;
42619         
42620         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
42621         var labelWidth = 100;
42622         
42623         if ((this.labelAlign != 'top')) {
42624             if (typeof this.labelWidth == 'number') {
42625                 labelWidth = this.labelWidth
42626             }
42627             this.padWidth =  20 + labelWidth;
42628             
42629         }
42630         
42631         Roo.form.Column.superclass.onRender.call(this, ct, position);
42632         if(this.width){
42633             this.el.setWidth(this.width);
42634         }
42635         if(this.height){
42636             this.el.setHeight(this.height);
42637         }
42638     },
42639     
42640     // private
42641     renderField : function(f){
42642         f.fieldEl = this.fieldTpl.append(this.el, [
42643                f.id, f.fieldLabel,
42644                f.labelStyle||this.labelStyle||'',
42645                this.elementStyle||'',
42646                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
42647                f.itemCls||this.itemCls||'',
42648                f.width ? f.width + this.padWidth : 160 + this.padWidth
42649        ],true);
42650     }
42651 });
42652  
42653
42654 /**
42655  * @class Roo.form.FieldSet
42656  * @extends Roo.form.Layout
42657  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
42658  * @constructor
42659  * @param {Object} config Configuration options
42660  */
42661 Roo.form.FieldSet = function(config){
42662     Roo.form.FieldSet.superclass.constructor.call(this, config);
42663 };
42664
42665 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
42666     /**
42667      * @cfg {String} legend
42668      * The text to display as the legend for the FieldSet (defaults to '')
42669      */
42670     /**
42671      * @cfg {String/Object} autoCreate
42672      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
42673      */
42674
42675     // private
42676     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
42677
42678     // private
42679     onRender : function(ct, position){
42680         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
42681         if(this.legend){
42682             this.setLegend(this.legend);
42683         }
42684     },
42685
42686     // private
42687     setLegend : function(text){
42688         if(this.rendered){
42689             this.el.child('legend').update(text);
42690         }
42691     }
42692 });/*
42693  * Based on:
42694  * Ext JS Library 1.1.1
42695  * Copyright(c) 2006-2007, Ext JS, LLC.
42696  *
42697  * Originally Released Under LGPL - original licence link has changed is not relivant.
42698  *
42699  * Fork - LGPL
42700  * <script type="text/javascript">
42701  */
42702 /**
42703  * @class Roo.form.VTypes
42704  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
42705  * @singleton
42706  */
42707 Roo.form.VTypes = function(){
42708     // closure these in so they are only created once.
42709     var alpha = /^[a-zA-Z_]+$/;
42710     var alphanum = /^[a-zA-Z0-9_]+$/;
42711     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
42712     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
42713
42714     // All these messages and functions are configurable
42715     return {
42716         /**
42717          * The function used to validate email addresses
42718          * @param {String} value The email address
42719          */
42720         'email' : function(v){
42721             return email.test(v);
42722         },
42723         /**
42724          * The error text to display when the email validation function returns false
42725          * @type String
42726          */
42727         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
42728         /**
42729          * The keystroke filter mask to be applied on email input
42730          * @type RegExp
42731          */
42732         'emailMask' : /[a-z0-9_\.\-@]/i,
42733
42734         /**
42735          * The function used to validate URLs
42736          * @param {String} value The URL
42737          */
42738         'url' : function(v){
42739             return url.test(v);
42740         },
42741         /**
42742          * The error text to display when the url validation function returns false
42743          * @type String
42744          */
42745         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
42746         
42747         /**
42748          * The function used to validate alpha values
42749          * @param {String} value The value
42750          */
42751         'alpha' : function(v){
42752             return alpha.test(v);
42753         },
42754         /**
42755          * The error text to display when the alpha validation function returns false
42756          * @type String
42757          */
42758         'alphaText' : 'This field should only contain letters and _',
42759         /**
42760          * The keystroke filter mask to be applied on alpha input
42761          * @type RegExp
42762          */
42763         'alphaMask' : /[a-z_]/i,
42764
42765         /**
42766          * The function used to validate alphanumeric values
42767          * @param {String} value The value
42768          */
42769         'alphanum' : function(v){
42770             return alphanum.test(v);
42771         },
42772         /**
42773          * The error text to display when the alphanumeric validation function returns false
42774          * @type String
42775          */
42776         'alphanumText' : 'This field should only contain letters, numbers and _',
42777         /**
42778          * The keystroke filter mask to be applied on alphanumeric input
42779          * @type RegExp
42780          */
42781         'alphanumMask' : /[a-z0-9_]/i
42782     };
42783 }();//<script type="text/javascript">
42784
42785 /**
42786  * @class Roo.form.FCKeditor
42787  * @extends Roo.form.TextArea
42788  * Wrapper around the FCKEditor http://www.fckeditor.net
42789  * @constructor
42790  * Creates a new FCKeditor
42791  * @param {Object} config Configuration options
42792  */
42793 Roo.form.FCKeditor = function(config){
42794     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42795     this.addEvents({
42796          /**
42797          * @event editorinit
42798          * Fired when the editor is initialized - you can add extra handlers here..
42799          * @param {FCKeditor} this
42800          * @param {Object} the FCK object.
42801          */
42802         editorinit : true
42803     });
42804     
42805     
42806 };
42807 Roo.form.FCKeditor.editors = { };
42808 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42809 {
42810     //defaultAutoCreate : {
42811     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42812     //},
42813     // private
42814     /**
42815      * @cfg {Object} fck options - see fck manual for details.
42816      */
42817     fckconfig : false,
42818     
42819     /**
42820      * @cfg {Object} fck toolbar set (Basic or Default)
42821      */
42822     toolbarSet : 'Basic',
42823     /**
42824      * @cfg {Object} fck BasePath
42825      */ 
42826     basePath : '/fckeditor/',
42827     
42828     
42829     frame : false,
42830     
42831     value : '',
42832     
42833    
42834     onRender : function(ct, position)
42835     {
42836         if(!this.el){
42837             this.defaultAutoCreate = {
42838                 tag: "textarea",
42839                 style:"width:300px;height:60px;",
42840                 autocomplete: "off"
42841             };
42842         }
42843         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42844         /*
42845         if(this.grow){
42846             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42847             if(this.preventScrollbars){
42848                 this.el.setStyle("overflow", "hidden");
42849             }
42850             this.el.setHeight(this.growMin);
42851         }
42852         */
42853         //console.log('onrender' + this.getId() );
42854         Roo.form.FCKeditor.editors[this.getId()] = this;
42855          
42856
42857         this.replaceTextarea() ;
42858         
42859     },
42860     
42861     getEditor : function() {
42862         return this.fckEditor;
42863     },
42864     /**
42865      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42866      * @param {Mixed} value The value to set
42867      */
42868     
42869     
42870     setValue : function(value)
42871     {
42872         //console.log('setValue: ' + value);
42873         
42874         if(typeof(value) == 'undefined') { // not sure why this is happending...
42875             return;
42876         }
42877         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42878         
42879         //if(!this.el || !this.getEditor()) {
42880         //    this.value = value;
42881             //this.setValue.defer(100,this,[value]);    
42882         //    return;
42883         //} 
42884         
42885         if(!this.getEditor()) {
42886             return;
42887         }
42888         
42889         this.getEditor().SetData(value);
42890         
42891         //
42892
42893     },
42894
42895     /**
42896      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42897      * @return {Mixed} value The field value
42898      */
42899     getValue : function()
42900     {
42901         
42902         if (this.frame && this.frame.dom.style.display == 'none') {
42903             return Roo.form.FCKeditor.superclass.getValue.call(this);
42904         }
42905         
42906         if(!this.el || !this.getEditor()) {
42907            
42908            // this.getValue.defer(100,this); 
42909             return this.value;
42910         }
42911        
42912         
42913         var value=this.getEditor().GetData();
42914         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42915         return Roo.form.FCKeditor.superclass.getValue.call(this);
42916         
42917
42918     },
42919
42920     /**
42921      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42922      * @return {Mixed} value The field value
42923      */
42924     getRawValue : function()
42925     {
42926         if (this.frame && this.frame.dom.style.display == 'none') {
42927             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42928         }
42929         
42930         if(!this.el || !this.getEditor()) {
42931             //this.getRawValue.defer(100,this); 
42932             return this.value;
42933             return;
42934         }
42935         
42936         
42937         
42938         var value=this.getEditor().GetData();
42939         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42940         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42941          
42942     },
42943     
42944     setSize : function(w,h) {
42945         
42946         
42947         
42948         //if (this.frame && this.frame.dom.style.display == 'none') {
42949         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42950         //    return;
42951         //}
42952         //if(!this.el || !this.getEditor()) {
42953         //    this.setSize.defer(100,this, [w,h]); 
42954         //    return;
42955         //}
42956         
42957         
42958         
42959         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42960         
42961         this.frame.dom.setAttribute('width', w);
42962         this.frame.dom.setAttribute('height', h);
42963         this.frame.setSize(w,h);
42964         
42965     },
42966     
42967     toggleSourceEdit : function(value) {
42968         
42969       
42970          
42971         this.el.dom.style.display = value ? '' : 'none';
42972         this.frame.dom.style.display = value ?  'none' : '';
42973         
42974     },
42975     
42976     
42977     focus: function(tag)
42978     {
42979         if (this.frame.dom.style.display == 'none') {
42980             return Roo.form.FCKeditor.superclass.focus.call(this);
42981         }
42982         if(!this.el || !this.getEditor()) {
42983             this.focus.defer(100,this, [tag]); 
42984             return;
42985         }
42986         
42987         
42988         
42989         
42990         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42991         this.getEditor().Focus();
42992         if (tgs.length) {
42993             if (!this.getEditor().Selection.GetSelection()) {
42994                 this.focus.defer(100,this, [tag]); 
42995                 return;
42996             }
42997             
42998             
42999             var r = this.getEditor().EditorDocument.createRange();
43000             r.setStart(tgs[0],0);
43001             r.setEnd(tgs[0],0);
43002             this.getEditor().Selection.GetSelection().removeAllRanges();
43003             this.getEditor().Selection.GetSelection().addRange(r);
43004             this.getEditor().Focus();
43005         }
43006         
43007     },
43008     
43009     
43010     
43011     replaceTextarea : function()
43012     {
43013         if ( document.getElementById( this.getId() + '___Frame' ) )
43014             return ;
43015         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
43016         //{
43017             // We must check the elements firstly using the Id and then the name.
43018         var oTextarea = document.getElementById( this.getId() );
43019         
43020         var colElementsByName = document.getElementsByName( this.getId() ) ;
43021          
43022         oTextarea.style.display = 'none' ;
43023
43024         if ( oTextarea.tabIndex ) {            
43025             this.TabIndex = oTextarea.tabIndex ;
43026         }
43027         
43028         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
43029         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
43030         this.frame = Roo.get(this.getId() + '___Frame')
43031     },
43032     
43033     _getConfigHtml : function()
43034     {
43035         var sConfig = '' ;
43036
43037         for ( var o in this.fckconfig ) {
43038             sConfig += sConfig.length > 0  ? '&amp;' : '';
43039             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
43040         }
43041
43042         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
43043     },
43044     
43045     
43046     _getIFrameHtml : function()
43047     {
43048         var sFile = 'fckeditor.html' ;
43049         /* no idea what this is about..
43050         try
43051         {
43052             if ( (/fcksource=true/i).test( window.top.location.search ) )
43053                 sFile = 'fckeditor.original.html' ;
43054         }
43055         catch (e) { 
43056         */
43057
43058         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
43059         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
43060         
43061         
43062         var html = '<iframe id="' + this.getId() +
43063             '___Frame" src="' + sLink +
43064             '" width="' + this.width +
43065             '" height="' + this.height + '"' +
43066             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
43067             ' frameborder="0" scrolling="no"></iframe>' ;
43068
43069         return html ;
43070     },
43071     
43072     _insertHtmlBefore : function( html, element )
43073     {
43074         if ( element.insertAdjacentHTML )       {
43075             // IE
43076             element.insertAdjacentHTML( 'beforeBegin', html ) ;
43077         } else { // Gecko
43078             var oRange = document.createRange() ;
43079             oRange.setStartBefore( element ) ;
43080             var oFragment = oRange.createContextualFragment( html );
43081             element.parentNode.insertBefore( oFragment, element ) ;
43082         }
43083     }
43084     
43085     
43086   
43087     
43088     
43089     
43090     
43091
43092 });
43093
43094 //Roo.reg('fckeditor', Roo.form.FCKeditor);
43095
43096 function FCKeditor_OnComplete(editorInstance){
43097     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
43098     f.fckEditor = editorInstance;
43099     //console.log("loaded");
43100     f.fireEvent('editorinit', f, editorInstance);
43101
43102   
43103
43104  
43105
43106
43107
43108
43109
43110
43111
43112
43113
43114
43115
43116
43117
43118
43119
43120 //<script type="text/javascript">
43121 /**
43122  * @class Roo.form.GridField
43123  * @extends Roo.form.Field
43124  * Embed a grid (or editable grid into a form)
43125  * STATUS ALPHA
43126  * 
43127  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
43128  * it needs 
43129  * xgrid.store = Roo.data.Store
43130  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
43131  * xgrid.store.reader = Roo.data.JsonReader 
43132  * 
43133  * 
43134  * @constructor
43135  * Creates a new GridField
43136  * @param {Object} config Configuration options
43137  */
43138 Roo.form.GridField = function(config){
43139     Roo.form.GridField.superclass.constructor.call(this, config);
43140      
43141 };
43142
43143 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
43144     /**
43145      * @cfg {Number} width  - used to restrict width of grid..
43146      */
43147     width : 100,
43148     /**
43149      * @cfg {Number} height - used to restrict height of grid..
43150      */
43151     height : 50,
43152      /**
43153      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
43154          * 
43155          *}
43156      */
43157     xgrid : false, 
43158     /**
43159      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43160      * {tag: "input", type: "checkbox", autocomplete: "off"})
43161      */
43162    // defaultAutoCreate : { tag: 'div' },
43163     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43164     /**
43165      * @cfg {String} addTitle Text to include for adding a title.
43166      */
43167     addTitle : false,
43168     //
43169     onResize : function(){
43170         Roo.form.Field.superclass.onResize.apply(this, arguments);
43171     },
43172
43173     initEvents : function(){
43174         // Roo.form.Checkbox.superclass.initEvents.call(this);
43175         // has no events...
43176        
43177     },
43178
43179
43180     getResizeEl : function(){
43181         return this.wrap;
43182     },
43183
43184     getPositionEl : function(){
43185         return this.wrap;
43186     },
43187
43188     // private
43189     onRender : function(ct, position){
43190         
43191         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
43192         var style = this.style;
43193         delete this.style;
43194         
43195         Roo.form.GridField.superclass.onRender.call(this, ct, position);
43196         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
43197         this.viewEl = this.wrap.createChild({ tag: 'div' });
43198         if (style) {
43199             this.viewEl.applyStyles(style);
43200         }
43201         if (this.width) {
43202             this.viewEl.setWidth(this.width);
43203         }
43204         if (this.height) {
43205             this.viewEl.setHeight(this.height);
43206         }
43207         //if(this.inputValue !== undefined){
43208         //this.setValue(this.value);
43209         
43210         
43211         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
43212         
43213         
43214         this.grid.render();
43215         this.grid.getDataSource().on('remove', this.refreshValue, this);
43216         this.grid.getDataSource().on('update', this.refreshValue, this);
43217         this.grid.on('afteredit', this.refreshValue, this);
43218  
43219     },
43220      
43221     
43222     /**
43223      * Sets the value of the item. 
43224      * @param {String} either an object  or a string..
43225      */
43226     setValue : function(v){
43227         //this.value = v;
43228         v = v || []; // empty set..
43229         // this does not seem smart - it really only affects memoryproxy grids..
43230         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
43231             var ds = this.grid.getDataSource();
43232             // assumes a json reader..
43233             var data = {}
43234             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
43235             ds.loadData( data);
43236         }
43237         // clear selection so it does not get stale.
43238         if (this.grid.sm) { 
43239             this.grid.sm.clearSelections();
43240         }
43241         
43242         Roo.form.GridField.superclass.setValue.call(this, v);
43243         this.refreshValue();
43244         // should load data in the grid really....
43245     },
43246     
43247     // private
43248     refreshValue: function() {
43249          var val = [];
43250         this.grid.getDataSource().each(function(r) {
43251             val.push(r.data);
43252         });
43253         this.el.dom.value = Roo.encode(val);
43254     }
43255     
43256      
43257     
43258     
43259 });/*
43260  * Based on:
43261  * Ext JS Library 1.1.1
43262  * Copyright(c) 2006-2007, Ext JS, LLC.
43263  *
43264  * Originally Released Under LGPL - original licence link has changed is not relivant.
43265  *
43266  * Fork - LGPL
43267  * <script type="text/javascript">
43268  */
43269 /**
43270  * @class Roo.form.DisplayField
43271  * @extends Roo.form.Field
43272  * A generic Field to display non-editable data.
43273  * @constructor
43274  * Creates a new Display Field item.
43275  * @param {Object} config Configuration options
43276  */
43277 Roo.form.DisplayField = function(config){
43278     Roo.form.DisplayField.superclass.constructor.call(this, config);
43279     
43280 };
43281
43282 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
43283     inputType:      'hidden',
43284     allowBlank:     true,
43285     readOnly:         true,
43286     
43287  
43288     /**
43289      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43290      */
43291     focusClass : undefined,
43292     /**
43293      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43294      */
43295     fieldClass: 'x-form-field',
43296     
43297      /**
43298      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
43299      */
43300     valueRenderer: undefined,
43301     
43302     width: 100,
43303     /**
43304      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43305      * {tag: "input", type: "checkbox", autocomplete: "off"})
43306      */
43307      
43308  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
43309
43310     onResize : function(){
43311         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
43312         
43313     },
43314
43315     initEvents : function(){
43316         // Roo.form.Checkbox.superclass.initEvents.call(this);
43317         // has no events...
43318        
43319     },
43320
43321
43322     getResizeEl : function(){
43323         return this.wrap;
43324     },
43325
43326     getPositionEl : function(){
43327         return this.wrap;
43328     },
43329
43330     // private
43331     onRender : function(ct, position){
43332         
43333         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
43334         //if(this.inputValue !== undefined){
43335         this.wrap = this.el.wrap();
43336         
43337         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
43338         
43339         if (this.bodyStyle) {
43340             this.viewEl.applyStyles(this.bodyStyle);
43341         }
43342         //this.viewEl.setStyle('padding', '2px');
43343         
43344         this.setValue(this.value);
43345         
43346     },
43347 /*
43348     // private
43349     initValue : Roo.emptyFn,
43350
43351   */
43352
43353         // private
43354     onClick : function(){
43355         
43356     },
43357
43358     /**
43359      * Sets the checked state of the checkbox.
43360      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
43361      */
43362     setValue : function(v){
43363         this.value = v;
43364         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
43365         // this might be called before we have a dom element..
43366         if (!this.viewEl) {
43367             return;
43368         }
43369         this.viewEl.dom.innerHTML = html;
43370         Roo.form.DisplayField.superclass.setValue.call(this, v);
43371
43372     }
43373 });/*
43374  * 
43375  * Licence- LGPL
43376  * 
43377  */
43378
43379 /**
43380  * @class Roo.form.DayPicker
43381  * @extends Roo.form.Field
43382  * A Day picker show [M] [T] [W] ....
43383  * @constructor
43384  * Creates a new Day Picker
43385  * @param {Object} config Configuration options
43386  */
43387 Roo.form.DayPicker= function(config){
43388     Roo.form.DayPicker.superclass.constructor.call(this, config);
43389      
43390 };
43391
43392 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
43393     /**
43394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43395      */
43396     focusClass : undefined,
43397     /**
43398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43399      */
43400     fieldClass: "x-form-field",
43401    
43402     /**
43403      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43404      * {tag: "input", type: "checkbox", autocomplete: "off"})
43405      */
43406     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43407     
43408    
43409     actionMode : 'viewEl', 
43410     //
43411     // private
43412  
43413     inputType : 'hidden',
43414     
43415      
43416     inputElement: false, // real input element?
43417     basedOn: false, // ????
43418     
43419     isFormField: true, // not sure where this is needed!!!!
43420
43421     onResize : function(){
43422         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43423         if(!this.boxLabel){
43424             this.el.alignTo(this.wrap, 'c-c');
43425         }
43426     },
43427
43428     initEvents : function(){
43429         Roo.form.Checkbox.superclass.initEvents.call(this);
43430         this.el.on("click", this.onClick,  this);
43431         this.el.on("change", this.onClick,  this);
43432     },
43433
43434
43435     getResizeEl : function(){
43436         return this.wrap;
43437     },
43438
43439     getPositionEl : function(){
43440         return this.wrap;
43441     },
43442
43443     
43444     // private
43445     onRender : function(ct, position){
43446         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43447        
43448         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
43449         
43450         var r1 = '<table><tr>';
43451         var r2 = '<tr class="x-form-daypick-icons">';
43452         for (var i=0; i < 7; i++) {
43453             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
43454             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
43455         }
43456         
43457         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
43458         viewEl.select('img').on('click', this.onClick, this);
43459         this.viewEl = viewEl;   
43460         
43461         
43462         // this will not work on Chrome!!!
43463         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43464         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43465         
43466         
43467           
43468
43469     },
43470
43471     // private
43472     initValue : Roo.emptyFn,
43473
43474     /**
43475      * Returns the checked state of the checkbox.
43476      * @return {Boolean} True if checked, else false
43477      */
43478     getValue : function(){
43479         return this.el.dom.value;
43480         
43481     },
43482
43483         // private
43484     onClick : function(e){ 
43485         //this.setChecked(!this.checked);
43486         Roo.get(e.target).toggleClass('x-menu-item-checked');
43487         this.refreshValue();
43488         //if(this.el.dom.checked != this.checked){
43489         //    this.setValue(this.el.dom.checked);
43490        // }
43491     },
43492     
43493     // private
43494     refreshValue : function()
43495     {
43496         var val = '';
43497         this.viewEl.select('img',true).each(function(e,i,n)  {
43498             val += e.is(".x-menu-item-checked") ? String(n) : '';
43499         });
43500         this.setValue(val, true);
43501     },
43502
43503     /**
43504      * Sets the checked state of the checkbox.
43505      * On is always based on a string comparison between inputValue and the param.
43506      * @param {Boolean/String} value - the value to set 
43507      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43508      */
43509     setValue : function(v,suppressEvent){
43510         if (!this.el.dom) {
43511             return;
43512         }
43513         var old = this.el.dom.value ;
43514         this.el.dom.value = v;
43515         if (suppressEvent) {
43516             return ;
43517         }
43518          
43519         // update display..
43520         this.viewEl.select('img',true).each(function(e,i,n)  {
43521             
43522             var on = e.is(".x-menu-item-checked");
43523             var newv = v.indexOf(String(n)) > -1;
43524             if (on != newv) {
43525                 e.toggleClass('x-menu-item-checked');
43526             }
43527             
43528         });
43529         
43530         
43531         this.fireEvent('change', this, v, old);
43532         
43533         
43534     },
43535    
43536     // handle setting of hidden value by some other method!!?!?
43537     setFromHidden: function()
43538     {
43539         if(!this.el){
43540             return;
43541         }
43542         //console.log("SET FROM HIDDEN");
43543         //alert('setFrom hidden');
43544         this.setValue(this.el.dom.value);
43545     },
43546     
43547     onDestroy : function()
43548     {
43549         if(this.viewEl){
43550             Roo.get(this.viewEl).remove();
43551         }
43552          
43553         Roo.form.DayPicker.superclass.onDestroy.call(this);
43554     }
43555
43556 });/*
43557  * RooJS Library 1.1.1
43558  * Copyright(c) 2008-2011  Alan Knowles
43559  *
43560  * License - LGPL
43561  */
43562  
43563
43564 /**
43565  * @class Roo.form.ComboCheck
43566  * @extends Roo.form.ComboBox
43567  * A combobox for multiple select items.
43568  *
43569  * FIXME - could do with a reset button..
43570  * 
43571  * @constructor
43572  * Create a new ComboCheck
43573  * @param {Object} config Configuration options
43574  */
43575 Roo.form.ComboCheck = function(config){
43576     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43577     // should verify some data...
43578     // like
43579     // hiddenName = required..
43580     // displayField = required
43581     // valudField == required
43582     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43583     var _t = this;
43584     Roo.each(req, function(e) {
43585         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43586             throw "Roo.form.ComboCheck : missing value for: " + e;
43587         }
43588     });
43589     
43590     
43591 };
43592
43593 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
43594      
43595      
43596     editable : false,
43597      
43598     selectedClass: 'x-menu-item-checked', 
43599     
43600     // private
43601     onRender : function(ct, position){
43602         var _t = this;
43603         
43604         
43605         
43606         if(!this.tpl){
43607             var cls = 'x-combo-list';
43608
43609             
43610             this.tpl =  new Roo.Template({
43611                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
43612                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
43613                    '<span>{' + this.displayField + '}</span>' +
43614                     '</div>' 
43615                 
43616             });
43617         }
43618  
43619         
43620         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
43621         this.view.singleSelect = false;
43622         this.view.multiSelect = true;
43623         this.view.toggleSelect = true;
43624         this.pageTb.add(new Roo.Toolbar.Fill(), {
43625             
43626             text: 'Done',
43627             handler: function()
43628             {
43629                 _t.collapse();
43630             }
43631         });
43632     },
43633     
43634     onViewOver : function(e, t){
43635         // do nothing...
43636         return;
43637         
43638     },
43639     
43640     onViewClick : function(doFocus,index){
43641         return;
43642         
43643     },
43644     select: function () {
43645         //Roo.log("SELECT CALLED");
43646     },
43647      
43648     selectByValue : function(xv, scrollIntoView){
43649         var ar = this.getValueArray();
43650         var sels = [];
43651         
43652         Roo.each(ar, function(v) {
43653             if(v === undefined || v === null){
43654                 return;
43655             }
43656             var r = this.findRecord(this.valueField, v);
43657             if(r){
43658                 sels.push(this.store.indexOf(r))
43659                 
43660             }
43661         },this);
43662         this.view.select(sels);
43663         return false;
43664     },
43665     
43666     
43667     
43668     onSelect : function(record, index){
43669        // Roo.log("onselect Called");
43670        // this is only called by the clear button now..
43671         this.view.clearSelections();
43672         this.setValue('[]');
43673         if (this.value != this.valueBefore) {
43674             this.fireEvent('change', this, this.value, this.valueBefore);
43675         }
43676     },
43677     getValueArray : function()
43678     {
43679         var ar = [] ;
43680         
43681         try {
43682             Roo.log(this.value);
43683             var ar = Roo.decode(this.value);
43684             return  ar instanceof Array ? ar : []; //?? valid?
43685             
43686         } catch(e) {
43687             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
43688             return [];
43689         }
43690          
43691     },
43692     expand : function ()
43693     {
43694         Roo.form.ComboCheck.superclass.expand.call(this);
43695         this.valueBefore = this.value;
43696         
43697
43698     },
43699     
43700     collapse : function(){
43701         Roo.form.ComboCheck.superclass.collapse.call(this);
43702         var sl = this.view.getSelectedIndexes();
43703         var st = this.store;
43704         var nv = [];
43705         var tv = [];
43706         var r;
43707         Roo.each(sl, function(i) {
43708             r = st.getAt(i);
43709             nv.push(r.get(this.valueField));
43710         },this);
43711         this.setValue(Roo.encode(nv));
43712         if (this.value != this.valueBefore) {
43713
43714             this.fireEvent('change', this, this.value, this.valueBefore);
43715         }
43716         
43717     },
43718     
43719     setValue : function(v){
43720         // Roo.log(v);
43721         this.value = v;
43722         
43723         var vals = this.getValueArray();
43724         var tv = [];
43725         Roo.each(vals, function(k) {
43726             var r = this.findRecord(this.valueField, k);
43727             if(r){
43728                 tv.push(r.data[this.displayField]);
43729             }else if(this.valueNotFoundText !== undefined){
43730                 tv.push( this.valueNotFoundText );
43731             }
43732         },this);
43733        // Roo.log(tv);
43734         
43735         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
43736         this.hiddenField.value = v;
43737         this.value = v;
43738     }
43739     
43740 });//<script type="text/javasscript">
43741  
43742
43743 /**
43744  * @class Roo.DDView
43745  * A DnD enabled version of Roo.View.
43746  * @param {Element/String} container The Element in which to create the View.
43747  * @param {String} tpl The template string used to create the markup for each element of the View
43748  * @param {Object} config The configuration properties. These include all the config options of
43749  * {@link Roo.View} plus some specific to this class.<br>
43750  * <p>
43751  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
43752  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
43753  * <p>
43754  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
43755 .x-view-drag-insert-above {
43756         border-top:1px dotted #3366cc;
43757 }
43758 .x-view-drag-insert-below {
43759         border-bottom:1px dotted #3366cc;
43760 }
43761 </code></pre>
43762  * 
43763  */
43764  
43765 Roo.DDView = function(container, tpl, config) {
43766     Roo.DDView.superclass.constructor.apply(this, arguments);
43767     this.getEl().setStyle("outline", "0px none");
43768     this.getEl().unselectable();
43769     if (this.dragGroup) {
43770                 this.setDraggable(this.dragGroup.split(","));
43771     }
43772     if (this.dropGroup) {
43773                 this.setDroppable(this.dropGroup.split(","));
43774     }
43775     if (this.deletable) {
43776         this.setDeletable();
43777     }
43778     this.isDirtyFlag = false;
43779         this.addEvents({
43780                 "drop" : true
43781         });
43782 };
43783
43784 Roo.extend(Roo.DDView, Roo.View, {
43785 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
43786 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
43787 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
43788 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
43789
43790         isFormField: true,
43791
43792         reset: Roo.emptyFn,
43793         
43794         clearInvalid: Roo.form.Field.prototype.clearInvalid,
43795
43796         validate: function() {
43797                 return true;
43798         },
43799         
43800         destroy: function() {
43801                 this.purgeListeners();
43802                 this.getEl.removeAllListeners();
43803                 this.getEl().remove();
43804                 if (this.dragZone) {
43805                         if (this.dragZone.destroy) {
43806                                 this.dragZone.destroy();
43807                         }
43808                 }
43809                 if (this.dropZone) {
43810                         if (this.dropZone.destroy) {
43811                                 this.dropZone.destroy();
43812                         }
43813                 }
43814         },
43815
43816 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
43817         getName: function() {
43818                 return this.name;
43819         },
43820
43821 /**     Loads the View from a JSON string representing the Records to put into the Store. */
43822         setValue: function(v) {
43823                 if (!this.store) {
43824                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
43825                 }
43826                 var data = {};
43827                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
43828                 this.store.proxy = new Roo.data.MemoryProxy(data);
43829                 this.store.load();
43830         },
43831
43832 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
43833         getValue: function() {
43834                 var result = '(';
43835                 this.store.each(function(rec) {
43836                         result += rec.id + ',';
43837                 });
43838                 return result.substr(0, result.length - 1) + ')';
43839         },
43840         
43841         getIds: function() {
43842                 var i = 0, result = new Array(this.store.getCount());
43843                 this.store.each(function(rec) {
43844                         result[i++] = rec.id;
43845                 });
43846                 return result;
43847         },
43848         
43849         isDirty: function() {
43850                 return this.isDirtyFlag;
43851         },
43852
43853 /**
43854  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
43855  *      whole Element becomes the target, and this causes the drop gesture to append.
43856  */
43857     getTargetFromEvent : function(e) {
43858                 var target = e.getTarget();
43859                 while ((target !== null) && (target.parentNode != this.el.dom)) {
43860                 target = target.parentNode;
43861                 }
43862                 if (!target) {
43863                         target = this.el.dom.lastChild || this.el.dom;
43864                 }
43865                 return target;
43866     },
43867
43868 /**
43869  *      Create the drag data which consists of an object which has the property "ddel" as
43870  *      the drag proxy element. 
43871  */
43872     getDragData : function(e) {
43873         var target = this.findItemFromChild(e.getTarget());
43874                 if(target) {
43875                         this.handleSelection(e);
43876                         var selNodes = this.getSelectedNodes();
43877             var dragData = {
43878                 source: this,
43879                 copy: this.copy || (this.allowCopy && e.ctrlKey),
43880                 nodes: selNodes,
43881                 records: []
43882                         };
43883                         var selectedIndices = this.getSelectedIndexes();
43884                         for (var i = 0; i < selectedIndices.length; i++) {
43885                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
43886                         }
43887                         if (selNodes.length == 1) {
43888                                 dragData.ddel = target.cloneNode(true); // the div element
43889                         } else {
43890                                 var div = document.createElement('div'); // create the multi element drag "ghost"
43891                                 div.className = 'multi-proxy';
43892                                 for (var i = 0, len = selNodes.length; i < len; i++) {
43893                                         div.appendChild(selNodes[i].cloneNode(true));
43894                                 }
43895                                 dragData.ddel = div;
43896                         }
43897             //console.log(dragData)
43898             //console.log(dragData.ddel.innerHTML)
43899                         return dragData;
43900                 }
43901         //console.log('nodragData')
43902                 return false;
43903     },
43904     
43905 /**     Specify to which ddGroup items in this DDView may be dragged. */
43906     setDraggable: function(ddGroup) {
43907         if (ddGroup instanceof Array) {
43908                 Roo.each(ddGroup, this.setDraggable, this);
43909                 return;
43910         }
43911         if (this.dragZone) {
43912                 this.dragZone.addToGroup(ddGroup);
43913         } else {
43914                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
43915                                 containerScroll: true,
43916                                 ddGroup: ddGroup 
43917
43918                         });
43919 //                      Draggability implies selection. DragZone's mousedown selects the element.
43920                         if (!this.multiSelect) { this.singleSelect = true; }
43921
43922 //                      Wire the DragZone's handlers up to methods in *this*
43923                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
43924                 }
43925     },
43926
43927 /**     Specify from which ddGroup this DDView accepts drops. */
43928     setDroppable: function(ddGroup) {
43929         if (ddGroup instanceof Array) {
43930                 Roo.each(ddGroup, this.setDroppable, this);
43931                 return;
43932         }
43933         if (this.dropZone) {
43934                 this.dropZone.addToGroup(ddGroup);
43935         } else {
43936                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
43937                                 containerScroll: true,
43938                                 ddGroup: ddGroup
43939                         });
43940
43941 //                      Wire the DropZone's handlers up to methods in *this*
43942                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
43943                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
43944                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
43945                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
43946                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
43947                 }
43948     },
43949
43950 /**     Decide whether to drop above or below a View node. */
43951     getDropPoint : function(e, n, dd){
43952         if (n == this.el.dom) { return "above"; }
43953                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
43954                 var c = t + (b - t) / 2;
43955                 var y = Roo.lib.Event.getPageY(e);
43956                 if(y <= c) {
43957                         return "above";
43958                 }else{
43959                         return "below";
43960                 }
43961     },
43962
43963     onNodeEnter : function(n, dd, e, data){
43964                 return false;
43965     },
43966     
43967     onNodeOver : function(n, dd, e, data){
43968                 var pt = this.getDropPoint(e, n, dd);
43969                 // set the insert point style on the target node
43970                 var dragElClass = this.dropNotAllowed;
43971                 if (pt) {
43972                         var targetElClass;
43973                         if (pt == "above"){
43974                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
43975                                 targetElClass = "x-view-drag-insert-above";
43976                         } else {
43977                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
43978                                 targetElClass = "x-view-drag-insert-below";
43979                         }
43980                         if (this.lastInsertClass != targetElClass){
43981                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
43982                                 this.lastInsertClass = targetElClass;
43983                         }
43984                 }
43985                 return dragElClass;
43986         },
43987
43988     onNodeOut : function(n, dd, e, data){
43989                 this.removeDropIndicators(n);
43990     },
43991
43992     onNodeDrop : function(n, dd, e, data){
43993         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
43994                 return false;
43995         }
43996         var pt = this.getDropPoint(e, n, dd);
43997                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
43998                 if (pt == "below") { insertAt++; }
43999                 for (var i = 0; i < data.records.length; i++) {
44000                         var r = data.records[i];
44001                         var dup = this.store.getById(r.id);
44002                         if (dup && (dd != this.dragZone)) {
44003                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
44004                         } else {
44005                                 if (data.copy) {
44006                                         this.store.insert(insertAt++, r.copy());
44007                                 } else {
44008                                         data.source.isDirtyFlag = true;
44009                                         r.store.remove(r);
44010                                         this.store.insert(insertAt++, r);
44011                                 }
44012                                 this.isDirtyFlag = true;
44013                         }
44014                 }
44015                 this.dragZone.cachedTarget = null;
44016                 return true;
44017     },
44018
44019     removeDropIndicators : function(n){
44020                 if(n){
44021                         Roo.fly(n).removeClass([
44022                                 "x-view-drag-insert-above",
44023                                 "x-view-drag-insert-below"]);
44024                         this.lastInsertClass = "_noclass";
44025                 }
44026     },
44027
44028 /**
44029  *      Utility method. Add a delete option to the DDView's context menu.
44030  *      @param {String} imageUrl The URL of the "delete" icon image.
44031  */
44032         setDeletable: function(imageUrl) {
44033                 if (!this.singleSelect && !this.multiSelect) {
44034                         this.singleSelect = true;
44035                 }
44036                 var c = this.getContextMenu();
44037                 this.contextMenu.on("itemclick", function(item) {
44038                         switch (item.id) {
44039                                 case "delete":
44040                                         this.remove(this.getSelectedIndexes());
44041                                         break;
44042                         }
44043                 }, this);
44044                 this.contextMenu.add({
44045                         icon: imageUrl,
44046                         id: "delete",
44047                         text: 'Delete'
44048                 });
44049         },
44050         
44051 /**     Return the context menu for this DDView. */
44052         getContextMenu: function() {
44053                 if (!this.contextMenu) {
44054 //                      Create the View's context menu
44055                         this.contextMenu = new Roo.menu.Menu({
44056                                 id: this.id + "-contextmenu"
44057                         });
44058                         this.el.on("contextmenu", this.showContextMenu, this);
44059                 }
44060                 return this.contextMenu;
44061         },
44062         
44063         disableContextMenu: function() {
44064                 if (this.contextMenu) {
44065                         this.el.un("contextmenu", this.showContextMenu, this);
44066                 }
44067         },
44068
44069         showContextMenu: function(e, item) {
44070         item = this.findItemFromChild(e.getTarget());
44071                 if (item) {
44072                         e.stopEvent();
44073                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
44074                         this.contextMenu.showAt(e.getXY());
44075             }
44076     },
44077
44078 /**
44079  *      Remove {@link Roo.data.Record}s at the specified indices.
44080  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
44081  */
44082     remove: function(selectedIndices) {
44083                 selectedIndices = [].concat(selectedIndices);
44084                 for (var i = 0; i < selectedIndices.length; i++) {
44085                         var rec = this.store.getAt(selectedIndices[i]);
44086                         this.store.remove(rec);
44087                 }
44088     },
44089
44090 /**
44091  *      Double click fires the event, but also, if this is draggable, and there is only one other
44092  *      related DropZone, it transfers the selected node.
44093  */
44094     onDblClick : function(e){
44095         var item = this.findItemFromChild(e.getTarget());
44096         if(item){
44097             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
44098                 return false;
44099             }
44100             if (this.dragGroup) {
44101                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
44102                     while (targets.indexOf(this.dropZone) > -1) {
44103                             targets.remove(this.dropZone);
44104                                 }
44105                     if (targets.length == 1) {
44106                                         this.dragZone.cachedTarget = null;
44107                         var el = Roo.get(targets[0].getEl());
44108                         var box = el.getBox(true);
44109                         targets[0].onNodeDrop(el.dom, {
44110                                 target: el.dom,
44111                                 xy: [box.x, box.y + box.height - 1]
44112                         }, null, this.getDragData(e));
44113                     }
44114                 }
44115         }
44116     },
44117     
44118     handleSelection: function(e) {
44119                 this.dragZone.cachedTarget = null;
44120         var item = this.findItemFromChild(e.getTarget());
44121         if (!item) {
44122                 this.clearSelections(true);
44123                 return;
44124         }
44125                 if (item && (this.multiSelect || this.singleSelect)){
44126                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
44127                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
44128                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
44129                                 this.unselect(item);
44130                         } else {
44131                                 this.select(item, this.multiSelect && e.ctrlKey);
44132                                 this.lastSelection = item;
44133                         }
44134                 }
44135     },
44136
44137     onItemClick : function(item, index, e){
44138                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
44139                         return false;
44140                 }
44141                 return true;
44142     },
44143
44144     unselect : function(nodeInfo, suppressEvent){
44145                 var node = this.getNode(nodeInfo);
44146                 if(node && this.isSelected(node)){
44147                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
44148                                 Roo.fly(node).removeClass(this.selectedClass);
44149                                 this.selections.remove(node);
44150                                 if(!suppressEvent){
44151                                         this.fireEvent("selectionchange", this, this.selections);
44152                                 }
44153                         }
44154                 }
44155     }
44156 });
44157 /*
44158  * Based on:
44159  * Ext JS Library 1.1.1
44160  * Copyright(c) 2006-2007, Ext JS, LLC.
44161  *
44162  * Originally Released Under LGPL - original licence link has changed is not relivant.
44163  *
44164  * Fork - LGPL
44165  * <script type="text/javascript">
44166  */
44167  
44168 /**
44169  * @class Roo.LayoutManager
44170  * @extends Roo.util.Observable
44171  * Base class for layout managers.
44172  */
44173 Roo.LayoutManager = function(container, config){
44174     Roo.LayoutManager.superclass.constructor.call(this);
44175     this.el = Roo.get(container);
44176     // ie scrollbar fix
44177     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
44178         document.body.scroll = "no";
44179     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
44180         this.el.position('relative');
44181     }
44182     this.id = this.el.id;
44183     this.el.addClass("x-layout-container");
44184     /** false to disable window resize monitoring @type Boolean */
44185     this.monitorWindowResize = true;
44186     this.regions = {};
44187     this.addEvents({
44188         /**
44189          * @event layout
44190          * Fires when a layout is performed. 
44191          * @param {Roo.LayoutManager} this
44192          */
44193         "layout" : true,
44194         /**
44195          * @event regionresized
44196          * Fires when the user resizes a region. 
44197          * @param {Roo.LayoutRegion} region The resized region
44198          * @param {Number} newSize The new size (width for east/west, height for north/south)
44199          */
44200         "regionresized" : true,
44201         /**
44202          * @event regioncollapsed
44203          * Fires when a region is collapsed. 
44204          * @param {Roo.LayoutRegion} region The collapsed region
44205          */
44206         "regioncollapsed" : true,
44207         /**
44208          * @event regionexpanded
44209          * Fires when a region is expanded.  
44210          * @param {Roo.LayoutRegion} region The expanded region
44211          */
44212         "regionexpanded" : true
44213     });
44214     this.updating = false;
44215     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
44216 };
44217
44218 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
44219     /**
44220      * Returns true if this layout is currently being updated
44221      * @return {Boolean}
44222      */
44223     isUpdating : function(){
44224         return this.updating; 
44225     },
44226     
44227     /**
44228      * Suspend the LayoutManager from doing auto-layouts while
44229      * making multiple add or remove calls
44230      */
44231     beginUpdate : function(){
44232         this.updating = true;    
44233     },
44234     
44235     /**
44236      * Restore auto-layouts and optionally disable the manager from performing a layout
44237      * @param {Boolean} noLayout true to disable a layout update 
44238      */
44239     endUpdate : function(noLayout){
44240         this.updating = false;
44241         if(!noLayout){
44242             this.layout();
44243         }    
44244     },
44245     
44246     layout: function(){
44247         
44248     },
44249     
44250     onRegionResized : function(region, newSize){
44251         this.fireEvent("regionresized", region, newSize);
44252         this.layout();
44253     },
44254     
44255     onRegionCollapsed : function(region){
44256         this.fireEvent("regioncollapsed", region);
44257     },
44258     
44259     onRegionExpanded : function(region){
44260         this.fireEvent("regionexpanded", region);
44261     },
44262         
44263     /**
44264      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
44265      * performs box-model adjustments.
44266      * @return {Object} The size as an object {width: (the width), height: (the height)}
44267      */
44268     getViewSize : function(){
44269         var size;
44270         if(this.el.dom != document.body){
44271             size = this.el.getSize();
44272         }else{
44273             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
44274         }
44275         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
44276         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44277         return size;
44278     },
44279     
44280     /**
44281      * Returns the Element this layout is bound to.
44282      * @return {Roo.Element}
44283      */
44284     getEl : function(){
44285         return this.el;
44286     },
44287     
44288     /**
44289      * Returns the specified region.
44290      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
44291      * @return {Roo.LayoutRegion}
44292      */
44293     getRegion : function(target){
44294         return this.regions[target.toLowerCase()];
44295     },
44296     
44297     onWindowResize : function(){
44298         if(this.monitorWindowResize){
44299             this.layout();
44300         }
44301     }
44302 });/*
44303  * Based on:
44304  * Ext JS Library 1.1.1
44305  * Copyright(c) 2006-2007, Ext JS, LLC.
44306  *
44307  * Originally Released Under LGPL - original licence link has changed is not relivant.
44308  *
44309  * Fork - LGPL
44310  * <script type="text/javascript">
44311  */
44312 /**
44313  * @class Roo.BorderLayout
44314  * @extends Roo.LayoutManager
44315  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
44316  * please see: <br><br>
44317  * <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>
44318  * <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>
44319  * Example:
44320  <pre><code>
44321  var layout = new Roo.BorderLayout(document.body, {
44322     north: {
44323         initialSize: 25,
44324         titlebar: false
44325     },
44326     west: {
44327         split:true,
44328         initialSize: 200,
44329         minSize: 175,
44330         maxSize: 400,
44331         titlebar: true,
44332         collapsible: true
44333     },
44334     east: {
44335         split:true,
44336         initialSize: 202,
44337         minSize: 175,
44338         maxSize: 400,
44339         titlebar: true,
44340         collapsible: true
44341     },
44342     south: {
44343         split:true,
44344         initialSize: 100,
44345         minSize: 100,
44346         maxSize: 200,
44347         titlebar: true,
44348         collapsible: true
44349     },
44350     center: {
44351         titlebar: true,
44352         autoScroll:true,
44353         resizeTabs: true,
44354         minTabWidth: 50,
44355         preferredTabWidth: 150
44356     }
44357 });
44358
44359 // shorthand
44360 var CP = Roo.ContentPanel;
44361
44362 layout.beginUpdate();
44363 layout.add("north", new CP("north", "North"));
44364 layout.add("south", new CP("south", {title: "South", closable: true}));
44365 layout.add("west", new CP("west", {title: "West"}));
44366 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
44367 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
44368 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
44369 layout.getRegion("center").showPanel("center1");
44370 layout.endUpdate();
44371 </code></pre>
44372
44373 <b>The container the layout is rendered into can be either the body element or any other element.
44374 If it is not the body element, the container needs to either be an absolute positioned element,
44375 or you will need to add "position:relative" to the css of the container.  You will also need to specify
44376 the container size if it is not the body element.</b>
44377
44378 * @constructor
44379 * Create a new BorderLayout
44380 * @param {String/HTMLElement/Element} container The container this layout is bound to
44381 * @param {Object} config Configuration options
44382  */
44383 Roo.BorderLayout = function(container, config){
44384     config = config || {};
44385     Roo.BorderLayout.superclass.constructor.call(this, container, config);
44386     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
44387     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
44388         var target = this.factory.validRegions[i];
44389         if(config[target]){
44390             this.addRegion(target, config[target]);
44391         }
44392     }
44393 };
44394
44395 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
44396     /**
44397      * Creates and adds a new region if it doesn't already exist.
44398      * @param {String} target The target region key (north, south, east, west or center).
44399      * @param {Object} config The regions config object
44400      * @return {BorderLayoutRegion} The new region
44401      */
44402     addRegion : function(target, config){
44403         if(!this.regions[target]){
44404             var r = this.factory.create(target, this, config);
44405             this.bindRegion(target, r);
44406         }
44407         return this.regions[target];
44408     },
44409
44410     // private (kinda)
44411     bindRegion : function(name, r){
44412         this.regions[name] = r;
44413         r.on("visibilitychange", this.layout, this);
44414         r.on("paneladded", this.layout, this);
44415         r.on("panelremoved", this.layout, this);
44416         r.on("invalidated", this.layout, this);
44417         r.on("resized", this.onRegionResized, this);
44418         r.on("collapsed", this.onRegionCollapsed, this);
44419         r.on("expanded", this.onRegionExpanded, this);
44420     },
44421
44422     /**
44423      * Performs a layout update.
44424      */
44425     layout : function(){
44426         if(this.updating) return;
44427         var size = this.getViewSize();
44428         var w = size.width;
44429         var h = size.height;
44430         var centerW = w;
44431         var centerH = h;
44432         var centerY = 0;
44433         var centerX = 0;
44434         //var x = 0, y = 0;
44435
44436         var rs = this.regions;
44437         var north = rs["north"];
44438         var south = rs["south"]; 
44439         var west = rs["west"];
44440         var east = rs["east"];
44441         var center = rs["center"];
44442         //if(this.hideOnLayout){ // not supported anymore
44443             //c.el.setStyle("display", "none");
44444         //}
44445         if(north && north.isVisible()){
44446             var b = north.getBox();
44447             var m = north.getMargins();
44448             b.width = w - (m.left+m.right);
44449             b.x = m.left;
44450             b.y = m.top;
44451             centerY = b.height + b.y + m.bottom;
44452             centerH -= centerY;
44453             north.updateBox(this.safeBox(b));
44454         }
44455         if(south && south.isVisible()){
44456             var b = south.getBox();
44457             var m = south.getMargins();
44458             b.width = w - (m.left+m.right);
44459             b.x = m.left;
44460             var totalHeight = (b.height + m.top + m.bottom);
44461             b.y = h - totalHeight + m.top;
44462             centerH -= totalHeight;
44463             south.updateBox(this.safeBox(b));
44464         }
44465         if(west && west.isVisible()){
44466             var b = west.getBox();
44467             var m = west.getMargins();
44468             b.height = centerH - (m.top+m.bottom);
44469             b.x = m.left;
44470             b.y = centerY + m.top;
44471             var totalWidth = (b.width + m.left + m.right);
44472             centerX += totalWidth;
44473             centerW -= totalWidth;
44474             west.updateBox(this.safeBox(b));
44475         }
44476         if(east && east.isVisible()){
44477             var b = east.getBox();
44478             var m = east.getMargins();
44479             b.height = centerH - (m.top+m.bottom);
44480             var totalWidth = (b.width + m.left + m.right);
44481             b.x = w - totalWidth + m.left;
44482             b.y = centerY + m.top;
44483             centerW -= totalWidth;
44484             east.updateBox(this.safeBox(b));
44485         }
44486         if(center){
44487             var m = center.getMargins();
44488             var centerBox = {
44489                 x: centerX + m.left,
44490                 y: centerY + m.top,
44491                 width: centerW - (m.left+m.right),
44492                 height: centerH - (m.top+m.bottom)
44493             };
44494             //if(this.hideOnLayout){
44495                 //center.el.setStyle("display", "block");
44496             //}
44497             center.updateBox(this.safeBox(centerBox));
44498         }
44499         this.el.repaint();
44500         this.fireEvent("layout", this);
44501     },
44502
44503     // private
44504     safeBox : function(box){
44505         box.width = Math.max(0, box.width);
44506         box.height = Math.max(0, box.height);
44507         return box;
44508     },
44509
44510     /**
44511      * Adds a ContentPanel (or subclass) to this layout.
44512      * @param {String} target The target region key (north, south, east, west or center).
44513      * @param {Roo.ContentPanel} panel The panel to add
44514      * @return {Roo.ContentPanel} The added panel
44515      */
44516     add : function(target, panel){
44517          
44518         target = target.toLowerCase();
44519         return this.regions[target].add(panel);
44520     },
44521
44522     /**
44523      * Remove a ContentPanel (or subclass) to this layout.
44524      * @param {String} target The target region key (north, south, east, west or center).
44525      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
44526      * @return {Roo.ContentPanel} The removed panel
44527      */
44528     remove : function(target, panel){
44529         target = target.toLowerCase();
44530         return this.regions[target].remove(panel);
44531     },
44532
44533     /**
44534      * Searches all regions for a panel with the specified id
44535      * @param {String} panelId
44536      * @return {Roo.ContentPanel} The panel or null if it wasn't found
44537      */
44538     findPanel : function(panelId){
44539         var rs = this.regions;
44540         for(var target in rs){
44541             if(typeof rs[target] != "function"){
44542                 var p = rs[target].getPanel(panelId);
44543                 if(p){
44544                     return p;
44545                 }
44546             }
44547         }
44548         return null;
44549     },
44550
44551     /**
44552      * Searches all regions for a panel with the specified id and activates (shows) it.
44553      * @param {String/ContentPanel} panelId The panels id or the panel itself
44554      * @return {Roo.ContentPanel} The shown panel or null
44555      */
44556     showPanel : function(panelId) {
44557       var rs = this.regions;
44558       for(var target in rs){
44559          var r = rs[target];
44560          if(typeof r != "function"){
44561             if(r.hasPanel(panelId)){
44562                return r.showPanel(panelId);
44563             }
44564          }
44565       }
44566       return null;
44567    },
44568
44569    /**
44570      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
44571      * @param {Roo.state.Provider} provider (optional) An alternate state provider
44572      */
44573     restoreState : function(provider){
44574         if(!provider){
44575             provider = Roo.state.Manager;
44576         }
44577         var sm = new Roo.LayoutStateManager();
44578         sm.init(this, provider);
44579     },
44580
44581     /**
44582      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
44583      * object should contain properties for each region to add ContentPanels to, and each property's value should be
44584      * a valid ContentPanel config object.  Example:
44585      * <pre><code>
44586 // Create the main layout
44587 var layout = new Roo.BorderLayout('main-ct', {
44588     west: {
44589         split:true,
44590         minSize: 175,
44591         titlebar: true
44592     },
44593     center: {
44594         title:'Components'
44595     }
44596 }, 'main-ct');
44597
44598 // Create and add multiple ContentPanels at once via configs
44599 layout.batchAdd({
44600    west: {
44601        id: 'source-files',
44602        autoCreate:true,
44603        title:'Ext Source Files',
44604        autoScroll:true,
44605        fitToFrame:true
44606    },
44607    center : {
44608        el: cview,
44609        autoScroll:true,
44610        fitToFrame:true,
44611        toolbar: tb,
44612        resizeEl:'cbody'
44613    }
44614 });
44615 </code></pre>
44616      * @param {Object} regions An object containing ContentPanel configs by region name
44617      */
44618     batchAdd : function(regions){
44619         this.beginUpdate();
44620         for(var rname in regions){
44621             var lr = this.regions[rname];
44622             if(lr){
44623                 this.addTypedPanels(lr, regions[rname]);
44624             }
44625         }
44626         this.endUpdate();
44627     },
44628
44629     // private
44630     addTypedPanels : function(lr, ps){
44631         if(typeof ps == 'string'){
44632             lr.add(new Roo.ContentPanel(ps));
44633         }
44634         else if(ps instanceof Array){
44635             for(var i =0, len = ps.length; i < len; i++){
44636                 this.addTypedPanels(lr, ps[i]);
44637             }
44638         }
44639         else if(!ps.events){ // raw config?
44640             var el = ps.el;
44641             delete ps.el; // prevent conflict
44642             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
44643         }
44644         else {  // panel object assumed!
44645             lr.add(ps);
44646         }
44647     },
44648     /**
44649      * Adds a xtype elements to the layout.
44650      * <pre><code>
44651
44652 layout.addxtype({
44653        xtype : 'ContentPanel',
44654        region: 'west',
44655        items: [ .... ]
44656    }
44657 );
44658
44659 layout.addxtype({
44660         xtype : 'NestedLayoutPanel',
44661         region: 'west',
44662         layout: {
44663            center: { },
44664            west: { }   
44665         },
44666         items : [ ... list of content panels or nested layout panels.. ]
44667    }
44668 );
44669 </code></pre>
44670      * @param {Object} cfg Xtype definition of item to add.
44671      */
44672     addxtype : function(cfg)
44673     {
44674         // basically accepts a pannel...
44675         // can accept a layout region..!?!?
44676         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
44677         
44678         if (!cfg.xtype.match(/Panel$/)) {
44679             return false;
44680         }
44681         var ret = false;
44682         
44683         if (typeof(cfg.region) == 'undefined') {
44684             Roo.log("Failed to add Panel, region was not set");
44685             Roo.log(cfg);
44686             return false;
44687         }
44688         var region = cfg.region;
44689         delete cfg.region;
44690         
44691           
44692         var xitems = [];
44693         if (cfg.items) {
44694             xitems = cfg.items;
44695             delete cfg.items;
44696         }
44697         
44698         
44699         switch(cfg.xtype) 
44700         {
44701             case 'ContentPanel':  // ContentPanel (el, cfg)
44702             case 'ScrollPanel':  // ContentPanel (el, cfg)
44703                 if(cfg.autoCreate) {
44704                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44705                 } else {
44706                     var el = this.el.createChild();
44707                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
44708                 }
44709                 
44710                 this.add(region, ret);
44711                 break;
44712             
44713             
44714             case 'TreePanel': // our new panel!
44715                 cfg.el = this.el.createChild();
44716                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
44717                 this.add(region, ret);
44718                 break;
44719             
44720             case 'NestedLayoutPanel': 
44721                 // create a new Layout (which is  a Border Layout...
44722                 var el = this.el.createChild();
44723                 var clayout = cfg.layout;
44724                 delete cfg.layout;
44725                 clayout.items   = clayout.items  || [];
44726                 // replace this exitems with the clayout ones..
44727                 xitems = clayout.items;
44728                  
44729                 
44730                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
44731                     cfg.background = false;
44732                 }
44733                 var layout = new Roo.BorderLayout(el, clayout);
44734                 
44735                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
44736                 //console.log('adding nested layout panel '  + cfg.toSource());
44737                 this.add(region, ret);
44738                 
44739                 break;
44740                 
44741             case 'GridPanel': 
44742             
44743                 // needs grid and region
44744                 
44745                 //var el = this.getRegion(region).el.createChild();
44746                 var el = this.el.createChild();
44747                 // create the grid first...
44748                 
44749                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
44750                 delete cfg.grid;
44751                 if (region == 'center' && this.active ) {
44752                     cfg.background = false;
44753                 }
44754                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
44755                 
44756                 this.add(region, ret);
44757                 if (cfg.background) {
44758                     ret.on('activate', function(gp) {
44759                         if (!gp.grid.rendered) {
44760                             gp.grid.render();
44761                         }
44762                     });
44763                 } else {
44764                     grid.render();
44765                 }
44766                 break;
44767            
44768                
44769                 
44770                 
44771             default: 
44772                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
44773                 return null;
44774              // GridPanel (grid, cfg)
44775             
44776         }
44777         this.beginUpdate();
44778         // add children..
44779         Roo.each(xitems, function(i)  {
44780             ret.addxtype(i);
44781         });
44782         this.endUpdate();
44783         return ret;
44784         
44785     }
44786 });
44787
44788 /**
44789  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
44790  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
44791  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
44792  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
44793  * <pre><code>
44794 // shorthand
44795 var CP = Roo.ContentPanel;
44796
44797 var layout = Roo.BorderLayout.create({
44798     north: {
44799         initialSize: 25,
44800         titlebar: false,
44801         panels: [new CP("north", "North")]
44802     },
44803     west: {
44804         split:true,
44805         initialSize: 200,
44806         minSize: 175,
44807         maxSize: 400,
44808         titlebar: true,
44809         collapsible: true,
44810         panels: [new CP("west", {title: "West"})]
44811     },
44812     east: {
44813         split:true,
44814         initialSize: 202,
44815         minSize: 175,
44816         maxSize: 400,
44817         titlebar: true,
44818         collapsible: true,
44819         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
44820     },
44821     south: {
44822         split:true,
44823         initialSize: 100,
44824         minSize: 100,
44825         maxSize: 200,
44826         titlebar: true,
44827         collapsible: true,
44828         panels: [new CP("south", {title: "South", closable: true})]
44829     },
44830     center: {
44831         titlebar: true,
44832         autoScroll:true,
44833         resizeTabs: true,
44834         minTabWidth: 50,
44835         preferredTabWidth: 150,
44836         panels: [
44837             new CP("center1", {title: "Close Me", closable: true}),
44838             new CP("center2", {title: "Center Panel", closable: false})
44839         ]
44840     }
44841 }, document.body);
44842
44843 layout.getRegion("center").showPanel("center1");
44844 </code></pre>
44845  * @param config
44846  * @param targetEl
44847  */
44848 Roo.BorderLayout.create = function(config, targetEl){
44849     var layout = new Roo.BorderLayout(targetEl || document.body, config);
44850     layout.beginUpdate();
44851     var regions = Roo.BorderLayout.RegionFactory.validRegions;
44852     for(var j = 0, jlen = regions.length; j < jlen; j++){
44853         var lr = regions[j];
44854         if(layout.regions[lr] && config[lr].panels){
44855             var r = layout.regions[lr];
44856             var ps = config[lr].panels;
44857             layout.addTypedPanels(r, ps);
44858         }
44859     }
44860     layout.endUpdate();
44861     return layout;
44862 };
44863
44864 // private
44865 Roo.BorderLayout.RegionFactory = {
44866     // private
44867     validRegions : ["north","south","east","west","center"],
44868
44869     // private
44870     create : function(target, mgr, config){
44871         target = target.toLowerCase();
44872         if(config.lightweight || config.basic){
44873             return new Roo.BasicLayoutRegion(mgr, config, target);
44874         }
44875         switch(target){
44876             case "north":
44877                 return new Roo.NorthLayoutRegion(mgr, config);
44878             case "south":
44879                 return new Roo.SouthLayoutRegion(mgr, config);
44880             case "east":
44881                 return new Roo.EastLayoutRegion(mgr, config);
44882             case "west":
44883                 return new Roo.WestLayoutRegion(mgr, config);
44884             case "center":
44885                 return new Roo.CenterLayoutRegion(mgr, config);
44886         }
44887         throw 'Layout region "'+target+'" not supported.';
44888     }
44889 };/*
44890  * Based on:
44891  * Ext JS Library 1.1.1
44892  * Copyright(c) 2006-2007, Ext JS, LLC.
44893  *
44894  * Originally Released Under LGPL - original licence link has changed is not relivant.
44895  *
44896  * Fork - LGPL
44897  * <script type="text/javascript">
44898  */
44899  
44900 /**
44901  * @class Roo.BasicLayoutRegion
44902  * @extends Roo.util.Observable
44903  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
44904  * and does not have a titlebar, tabs or any other features. All it does is size and position 
44905  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
44906  */
44907 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
44908     this.mgr = mgr;
44909     this.position  = pos;
44910     this.events = {
44911         /**
44912          * @scope Roo.BasicLayoutRegion
44913          */
44914         
44915         /**
44916          * @event beforeremove
44917          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
44918          * @param {Roo.LayoutRegion} this
44919          * @param {Roo.ContentPanel} panel The panel
44920          * @param {Object} e The cancel event object
44921          */
44922         "beforeremove" : true,
44923         /**
44924          * @event invalidated
44925          * Fires when the layout for this region is changed.
44926          * @param {Roo.LayoutRegion} this
44927          */
44928         "invalidated" : true,
44929         /**
44930          * @event visibilitychange
44931          * Fires when this region is shown or hidden 
44932          * @param {Roo.LayoutRegion} this
44933          * @param {Boolean} visibility true or false
44934          */
44935         "visibilitychange" : true,
44936         /**
44937          * @event paneladded
44938          * Fires when a panel is added. 
44939          * @param {Roo.LayoutRegion} this
44940          * @param {Roo.ContentPanel} panel The panel
44941          */
44942         "paneladded" : true,
44943         /**
44944          * @event panelremoved
44945          * Fires when a panel is removed. 
44946          * @param {Roo.LayoutRegion} this
44947          * @param {Roo.ContentPanel} panel The panel
44948          */
44949         "panelremoved" : true,
44950         /**
44951          * @event collapsed
44952          * Fires when this region is collapsed.
44953          * @param {Roo.LayoutRegion} this
44954          */
44955         "collapsed" : true,
44956         /**
44957          * @event expanded
44958          * Fires when this region is expanded.
44959          * @param {Roo.LayoutRegion} this
44960          */
44961         "expanded" : true,
44962         /**
44963          * @event slideshow
44964          * Fires when this region is slid into view.
44965          * @param {Roo.LayoutRegion} this
44966          */
44967         "slideshow" : true,
44968         /**
44969          * @event slidehide
44970          * Fires when this region slides out of view. 
44971          * @param {Roo.LayoutRegion} this
44972          */
44973         "slidehide" : true,
44974         /**
44975          * @event panelactivated
44976          * Fires when a panel is activated. 
44977          * @param {Roo.LayoutRegion} this
44978          * @param {Roo.ContentPanel} panel The activated panel
44979          */
44980         "panelactivated" : true,
44981         /**
44982          * @event resized
44983          * Fires when the user resizes this region. 
44984          * @param {Roo.LayoutRegion} this
44985          * @param {Number} newSize The new size (width for east/west, height for north/south)
44986          */
44987         "resized" : true
44988     };
44989     /** A collection of panels in this region. @type Roo.util.MixedCollection */
44990     this.panels = new Roo.util.MixedCollection();
44991     this.panels.getKey = this.getPanelId.createDelegate(this);
44992     this.box = null;
44993     this.activePanel = null;
44994     // ensure listeners are added...
44995     
44996     if (config.listeners || config.events) {
44997         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
44998             listeners : config.listeners || {},
44999             events : config.events || {}
45000         });
45001     }
45002     
45003     if(skipConfig !== true){
45004         this.applyConfig(config);
45005     }
45006 };
45007
45008 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
45009     getPanelId : function(p){
45010         return p.getId();
45011     },
45012     
45013     applyConfig : function(config){
45014         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45015         this.config = config;
45016         
45017     },
45018     
45019     /**
45020      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
45021      * the width, for horizontal (north, south) the height.
45022      * @param {Number} newSize The new width or height
45023      */
45024     resizeTo : function(newSize){
45025         var el = this.el ? this.el :
45026                  (this.activePanel ? this.activePanel.getEl() : null);
45027         if(el){
45028             switch(this.position){
45029                 case "east":
45030                 case "west":
45031                     el.setWidth(newSize);
45032                     this.fireEvent("resized", this, newSize);
45033                 break;
45034                 case "north":
45035                 case "south":
45036                     el.setHeight(newSize);
45037                     this.fireEvent("resized", this, newSize);
45038                 break;                
45039             }
45040         }
45041     },
45042     
45043     getBox : function(){
45044         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
45045     },
45046     
45047     getMargins : function(){
45048         return this.margins;
45049     },
45050     
45051     updateBox : function(box){
45052         this.box = box;
45053         var el = this.activePanel.getEl();
45054         el.dom.style.left = box.x + "px";
45055         el.dom.style.top = box.y + "px";
45056         this.activePanel.setSize(box.width, box.height);
45057     },
45058     
45059     /**
45060      * Returns the container element for this region.
45061      * @return {Roo.Element}
45062      */
45063     getEl : function(){
45064         return this.activePanel;
45065     },
45066     
45067     /**
45068      * Returns true if this region is currently visible.
45069      * @return {Boolean}
45070      */
45071     isVisible : function(){
45072         return this.activePanel ? true : false;
45073     },
45074     
45075     setActivePanel : function(panel){
45076         panel = this.getPanel(panel);
45077         if(this.activePanel && this.activePanel != panel){
45078             this.activePanel.setActiveState(false);
45079             this.activePanel.getEl().setLeftTop(-10000,-10000);
45080         }
45081         this.activePanel = panel;
45082         panel.setActiveState(true);
45083         if(this.box){
45084             panel.setSize(this.box.width, this.box.height);
45085         }
45086         this.fireEvent("panelactivated", this, panel);
45087         this.fireEvent("invalidated");
45088     },
45089     
45090     /**
45091      * Show the specified panel.
45092      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
45093      * @return {Roo.ContentPanel} The shown panel or null
45094      */
45095     showPanel : function(panel){
45096         if(panel = this.getPanel(panel)){
45097             this.setActivePanel(panel);
45098         }
45099         return panel;
45100     },
45101     
45102     /**
45103      * Get the active panel for this region.
45104      * @return {Roo.ContentPanel} The active panel or null
45105      */
45106     getActivePanel : function(){
45107         return this.activePanel;
45108     },
45109     
45110     /**
45111      * Add the passed ContentPanel(s)
45112      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45113      * @return {Roo.ContentPanel} The panel added (if only one was added)
45114      */
45115     add : function(panel){
45116         if(arguments.length > 1){
45117             for(var i = 0, len = arguments.length; i < len; i++) {
45118                 this.add(arguments[i]);
45119             }
45120             return null;
45121         }
45122         if(this.hasPanel(panel)){
45123             this.showPanel(panel);
45124             return panel;
45125         }
45126         var el = panel.getEl();
45127         if(el.dom.parentNode != this.mgr.el.dom){
45128             this.mgr.el.dom.appendChild(el.dom);
45129         }
45130         if(panel.setRegion){
45131             panel.setRegion(this);
45132         }
45133         this.panels.add(panel);
45134         el.setStyle("position", "absolute");
45135         if(!panel.background){
45136             this.setActivePanel(panel);
45137             if(this.config.initialSize && this.panels.getCount()==1){
45138                 this.resizeTo(this.config.initialSize);
45139             }
45140         }
45141         this.fireEvent("paneladded", this, panel);
45142         return panel;
45143     },
45144     
45145     /**
45146      * Returns true if the panel is in this region.
45147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45148      * @return {Boolean}
45149      */
45150     hasPanel : function(panel){
45151         if(typeof panel == "object"){ // must be panel obj
45152             panel = panel.getId();
45153         }
45154         return this.getPanel(panel) ? true : false;
45155     },
45156     
45157     /**
45158      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45159      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45160      * @param {Boolean} preservePanel Overrides the config preservePanel option
45161      * @return {Roo.ContentPanel} The panel that was removed
45162      */
45163     remove : function(panel, preservePanel){
45164         panel = this.getPanel(panel);
45165         if(!panel){
45166             return null;
45167         }
45168         var e = {};
45169         this.fireEvent("beforeremove", this, panel, e);
45170         if(e.cancel === true){
45171             return null;
45172         }
45173         var panelId = panel.getId();
45174         this.panels.removeKey(panelId);
45175         return panel;
45176     },
45177     
45178     /**
45179      * Returns the panel specified or null if it's not in this region.
45180      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
45181      * @return {Roo.ContentPanel}
45182      */
45183     getPanel : function(id){
45184         if(typeof id == "object"){ // must be panel obj
45185             return id;
45186         }
45187         return this.panels.get(id);
45188     },
45189     
45190     /**
45191      * Returns this regions position (north/south/east/west/center).
45192      * @return {String} 
45193      */
45194     getPosition: function(){
45195         return this.position;    
45196     }
45197 });/*
45198  * Based on:
45199  * Ext JS Library 1.1.1
45200  * Copyright(c) 2006-2007, Ext JS, LLC.
45201  *
45202  * Originally Released Under LGPL - original licence link has changed is not relivant.
45203  *
45204  * Fork - LGPL
45205  * <script type="text/javascript">
45206  */
45207  
45208 /**
45209  * @class Roo.LayoutRegion
45210  * @extends Roo.BasicLayoutRegion
45211  * This class represents a region in a layout manager.
45212  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
45213  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
45214  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
45215  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
45216  * @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})
45217  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
45218  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
45219  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
45220  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
45221  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
45222  * @cfg {String}    title           The title for the region (overrides panel titles)
45223  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
45224  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
45225  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
45226  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
45227  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
45228  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
45229  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
45230  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
45231  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
45232  * @cfg {Boolean}   showPin         True to show a pin button
45233  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
45234  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
45235  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
45236  * @cfg {Number}    width           For East/West panels
45237  * @cfg {Number}    height          For North/South panels
45238  * @cfg {Boolean}   split           To show the splitter
45239  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
45240  */
45241 Roo.LayoutRegion = function(mgr, config, pos){
45242     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
45243     var dh = Roo.DomHelper;
45244     /** This region's container element 
45245     * @type Roo.Element */
45246     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
45247     /** This region's title element 
45248     * @type Roo.Element */
45249
45250     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
45251         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
45252         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
45253     ]}, true);
45254     this.titleEl.enableDisplayMode();
45255     /** This region's title text element 
45256     * @type HTMLElement */
45257     this.titleTextEl = this.titleEl.dom.firstChild;
45258     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
45259     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
45260     this.closeBtn.enableDisplayMode();
45261     this.closeBtn.on("click", this.closeClicked, this);
45262     this.closeBtn.hide();
45263
45264     this.createBody(config);
45265     this.visible = true;
45266     this.collapsed = false;
45267
45268     if(config.hideWhenEmpty){
45269         this.hide();
45270         this.on("paneladded", this.validateVisibility, this);
45271         this.on("panelremoved", this.validateVisibility, this);
45272     }
45273     this.applyConfig(config);
45274 };
45275
45276 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
45277
45278     createBody : function(){
45279         /** This region's body element 
45280         * @type Roo.Element */
45281         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
45282     },
45283
45284     applyConfig : function(c){
45285         if(c.collapsible && this.position != "center" && !this.collapsedEl){
45286             var dh = Roo.DomHelper;
45287             if(c.titlebar !== false){
45288                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
45289                 this.collapseBtn.on("click", this.collapse, this);
45290                 this.collapseBtn.enableDisplayMode();
45291
45292                 if(c.showPin === true || this.showPin){
45293                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
45294                     this.stickBtn.enableDisplayMode();
45295                     this.stickBtn.on("click", this.expand, this);
45296                     this.stickBtn.hide();
45297                 }
45298             }
45299             /** This region's collapsed element
45300             * @type Roo.Element */
45301             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
45302                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
45303             ]}, true);
45304             if(c.floatable !== false){
45305                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
45306                this.collapsedEl.on("click", this.collapseClick, this);
45307             }
45308
45309             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
45310                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
45311                    id: "message", unselectable: "on", style:{"float":"left"}});
45312                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
45313              }
45314             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
45315             this.expandBtn.on("click", this.expand, this);
45316         }
45317         if(this.collapseBtn){
45318             this.collapseBtn.setVisible(c.collapsible == true);
45319         }
45320         this.cmargins = c.cmargins || this.cmargins ||
45321                          (this.position == "west" || this.position == "east" ?
45322                              {top: 0, left: 2, right:2, bottom: 0} :
45323                              {top: 2, left: 0, right:0, bottom: 2});
45324         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
45325         this.bottomTabs = c.tabPosition != "top";
45326         this.autoScroll = c.autoScroll || false;
45327         if(this.autoScroll){
45328             this.bodyEl.setStyle("overflow", "auto");
45329         }else{
45330             this.bodyEl.setStyle("overflow", "hidden");
45331         }
45332         //if(c.titlebar !== false){
45333             if((!c.titlebar && !c.title) || c.titlebar === false){
45334                 this.titleEl.hide();
45335             }else{
45336                 this.titleEl.show();
45337                 if(c.title){
45338                     this.titleTextEl.innerHTML = c.title;
45339                 }
45340             }
45341         //}
45342         this.duration = c.duration || .30;
45343         this.slideDuration = c.slideDuration || .45;
45344         this.config = c;
45345         if(c.collapsed){
45346             this.collapse(true);
45347         }
45348         if(c.hidden){
45349             this.hide();
45350         }
45351     },
45352     /**
45353      * Returns true if this region is currently visible.
45354      * @return {Boolean}
45355      */
45356     isVisible : function(){
45357         return this.visible;
45358     },
45359
45360     /**
45361      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
45362      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
45363      */
45364     setCollapsedTitle : function(title){
45365         title = title || "&#160;";
45366         if(this.collapsedTitleTextEl){
45367             this.collapsedTitleTextEl.innerHTML = title;
45368         }
45369     },
45370
45371     getBox : function(){
45372         var b;
45373         if(!this.collapsed){
45374             b = this.el.getBox(false, true);
45375         }else{
45376             b = this.collapsedEl.getBox(false, true);
45377         }
45378         return b;
45379     },
45380
45381     getMargins : function(){
45382         return this.collapsed ? this.cmargins : this.margins;
45383     },
45384
45385     highlight : function(){
45386         this.el.addClass("x-layout-panel-dragover");
45387     },
45388
45389     unhighlight : function(){
45390         this.el.removeClass("x-layout-panel-dragover");
45391     },
45392
45393     updateBox : function(box){
45394         this.box = box;
45395         if(!this.collapsed){
45396             this.el.dom.style.left = box.x + "px";
45397             this.el.dom.style.top = box.y + "px";
45398             this.updateBody(box.width, box.height);
45399         }else{
45400             this.collapsedEl.dom.style.left = box.x + "px";
45401             this.collapsedEl.dom.style.top = box.y + "px";
45402             this.collapsedEl.setSize(box.width, box.height);
45403         }
45404         if(this.tabs){
45405             this.tabs.autoSizeTabs();
45406         }
45407     },
45408
45409     updateBody : function(w, h){
45410         if(w !== null){
45411             this.el.setWidth(w);
45412             w -= this.el.getBorderWidth("rl");
45413             if(this.config.adjustments){
45414                 w += this.config.adjustments[0];
45415             }
45416         }
45417         if(h !== null){
45418             this.el.setHeight(h);
45419             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
45420             h -= this.el.getBorderWidth("tb");
45421             if(this.config.adjustments){
45422                 h += this.config.adjustments[1];
45423             }
45424             this.bodyEl.setHeight(h);
45425             if(this.tabs){
45426                 h = this.tabs.syncHeight(h);
45427             }
45428         }
45429         if(this.panelSize){
45430             w = w !== null ? w : this.panelSize.width;
45431             h = h !== null ? h : this.panelSize.height;
45432         }
45433         if(this.activePanel){
45434             var el = this.activePanel.getEl();
45435             w = w !== null ? w : el.getWidth();
45436             h = h !== null ? h : el.getHeight();
45437             this.panelSize = {width: w, height: h};
45438             this.activePanel.setSize(w, h);
45439         }
45440         if(Roo.isIE && this.tabs){
45441             this.tabs.el.repaint();
45442         }
45443     },
45444
45445     /**
45446      * Returns the container element for this region.
45447      * @return {Roo.Element}
45448      */
45449     getEl : function(){
45450         return this.el;
45451     },
45452
45453     /**
45454      * Hides this region.
45455      */
45456     hide : function(){
45457         if(!this.collapsed){
45458             this.el.dom.style.left = "-2000px";
45459             this.el.hide();
45460         }else{
45461             this.collapsedEl.dom.style.left = "-2000px";
45462             this.collapsedEl.hide();
45463         }
45464         this.visible = false;
45465         this.fireEvent("visibilitychange", this, false);
45466     },
45467
45468     /**
45469      * Shows this region if it was previously hidden.
45470      */
45471     show : function(){
45472         if(!this.collapsed){
45473             this.el.show();
45474         }else{
45475             this.collapsedEl.show();
45476         }
45477         this.visible = true;
45478         this.fireEvent("visibilitychange", this, true);
45479     },
45480
45481     closeClicked : function(){
45482         if(this.activePanel){
45483             this.remove(this.activePanel);
45484         }
45485     },
45486
45487     collapseClick : function(e){
45488         if(this.isSlid){
45489            e.stopPropagation();
45490            this.slideIn();
45491         }else{
45492            e.stopPropagation();
45493            this.slideOut();
45494         }
45495     },
45496
45497     /**
45498      * Collapses this region.
45499      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
45500      */
45501     collapse : function(skipAnim){
45502         if(this.collapsed) return;
45503         this.collapsed = true;
45504         if(this.split){
45505             this.split.el.hide();
45506         }
45507         if(this.config.animate && skipAnim !== true){
45508             this.fireEvent("invalidated", this);
45509             this.animateCollapse();
45510         }else{
45511             this.el.setLocation(-20000,-20000);
45512             this.el.hide();
45513             this.collapsedEl.show();
45514             this.fireEvent("collapsed", this);
45515             this.fireEvent("invalidated", this);
45516         }
45517     },
45518
45519     animateCollapse : function(){
45520         // overridden
45521     },
45522
45523     /**
45524      * Expands this region if it was previously collapsed.
45525      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
45526      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
45527      */
45528     expand : function(e, skipAnim){
45529         if(e) e.stopPropagation();
45530         if(!this.collapsed || this.el.hasActiveFx()) return;
45531         if(this.isSlid){
45532             this.afterSlideIn();
45533             skipAnim = true;
45534         }
45535         this.collapsed = false;
45536         if(this.config.animate && skipAnim !== true){
45537             this.animateExpand();
45538         }else{
45539             this.el.show();
45540             if(this.split){
45541                 this.split.el.show();
45542             }
45543             this.collapsedEl.setLocation(-2000,-2000);
45544             this.collapsedEl.hide();
45545             this.fireEvent("invalidated", this);
45546             this.fireEvent("expanded", this);
45547         }
45548     },
45549
45550     animateExpand : function(){
45551         // overridden
45552     },
45553
45554     initTabs : function()
45555     {
45556         this.bodyEl.setStyle("overflow", "hidden");
45557         var ts = new Roo.TabPanel(
45558                 this.bodyEl.dom,
45559                 {
45560                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
45561                     disableTooltips: this.config.disableTabTips,
45562                     toolbar : this.config.toolbar
45563                 }
45564         );
45565         if(this.config.hideTabs){
45566             ts.stripWrap.setDisplayed(false);
45567         }
45568         this.tabs = ts;
45569         ts.resizeTabs = this.config.resizeTabs === true;
45570         ts.minTabWidth = this.config.minTabWidth || 40;
45571         ts.maxTabWidth = this.config.maxTabWidth || 250;
45572         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
45573         ts.monitorResize = false;
45574         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45575         ts.bodyEl.addClass('x-layout-tabs-body');
45576         this.panels.each(this.initPanelAsTab, this);
45577     },
45578
45579     initPanelAsTab : function(panel){
45580         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
45581                     this.config.closeOnTab && panel.isClosable());
45582         if(panel.tabTip !== undefined){
45583             ti.setTooltip(panel.tabTip);
45584         }
45585         ti.on("activate", function(){
45586               this.setActivePanel(panel);
45587         }, this);
45588         if(this.config.closeOnTab){
45589             ti.on("beforeclose", function(t, e){
45590                 e.cancel = true;
45591                 this.remove(panel);
45592             }, this);
45593         }
45594         return ti;
45595     },
45596
45597     updatePanelTitle : function(panel, title){
45598         if(this.activePanel == panel){
45599             this.updateTitle(title);
45600         }
45601         if(this.tabs){
45602             var ti = this.tabs.getTab(panel.getEl().id);
45603             ti.setText(title);
45604             if(panel.tabTip !== undefined){
45605                 ti.setTooltip(panel.tabTip);
45606             }
45607         }
45608     },
45609
45610     updateTitle : function(title){
45611         if(this.titleTextEl && !this.config.title){
45612             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
45613         }
45614     },
45615
45616     setActivePanel : function(panel){
45617         panel = this.getPanel(panel);
45618         if(this.activePanel && this.activePanel != panel){
45619             this.activePanel.setActiveState(false);
45620         }
45621         this.activePanel = panel;
45622         panel.setActiveState(true);
45623         if(this.panelSize){
45624             panel.setSize(this.panelSize.width, this.panelSize.height);
45625         }
45626         if(this.closeBtn){
45627             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
45628         }
45629         this.updateTitle(panel.getTitle());
45630         if(this.tabs){
45631             this.fireEvent("invalidated", this);
45632         }
45633         this.fireEvent("panelactivated", this, panel);
45634     },
45635
45636     /**
45637      * Shows the specified panel.
45638      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
45639      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
45640      */
45641     showPanel : function(panel){
45642         if(panel = this.getPanel(panel)){
45643             if(this.tabs){
45644                 var tab = this.tabs.getTab(panel.getEl().id);
45645                 if(tab.isHidden()){
45646                     this.tabs.unhideTab(tab.id);
45647                 }
45648                 tab.activate();
45649             }else{
45650                 this.setActivePanel(panel);
45651             }
45652         }
45653         return panel;
45654     },
45655
45656     /**
45657      * Get the active panel for this region.
45658      * @return {Roo.ContentPanel} The active panel or null
45659      */
45660     getActivePanel : function(){
45661         return this.activePanel;
45662     },
45663
45664     validateVisibility : function(){
45665         if(this.panels.getCount() < 1){
45666             this.updateTitle("&#160;");
45667             this.closeBtn.hide();
45668             this.hide();
45669         }else{
45670             if(!this.isVisible()){
45671                 this.show();
45672             }
45673         }
45674     },
45675
45676     /**
45677      * Adds the passed ContentPanel(s) to this region.
45678      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
45679      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
45680      */
45681     add : function(panel){
45682         if(arguments.length > 1){
45683             for(var i = 0, len = arguments.length; i < len; i++) {
45684                 this.add(arguments[i]);
45685             }
45686             return null;
45687         }
45688         if(this.hasPanel(panel)){
45689             this.showPanel(panel);
45690             return panel;
45691         }
45692         panel.setRegion(this);
45693         this.panels.add(panel);
45694         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
45695             this.bodyEl.dom.appendChild(panel.getEl().dom);
45696             if(panel.background !== true){
45697                 this.setActivePanel(panel);
45698             }
45699             this.fireEvent("paneladded", this, panel);
45700             return panel;
45701         }
45702         if(!this.tabs){
45703             this.initTabs();
45704         }else{
45705             this.initPanelAsTab(panel);
45706         }
45707         if(panel.background !== true){
45708             this.tabs.activate(panel.getEl().id);
45709         }
45710         this.fireEvent("paneladded", this, panel);
45711         return panel;
45712     },
45713
45714     /**
45715      * Hides the tab for the specified panel.
45716      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45717      */
45718     hidePanel : function(panel){
45719         if(this.tabs && (panel = this.getPanel(panel))){
45720             this.tabs.hideTab(panel.getEl().id);
45721         }
45722     },
45723
45724     /**
45725      * Unhides the tab for a previously hidden panel.
45726      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45727      */
45728     unhidePanel : function(panel){
45729         if(this.tabs && (panel = this.getPanel(panel))){
45730             this.tabs.unhideTab(panel.getEl().id);
45731         }
45732     },
45733
45734     clearPanels : function(){
45735         while(this.panels.getCount() > 0){
45736              this.remove(this.panels.first());
45737         }
45738     },
45739
45740     /**
45741      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
45742      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
45743      * @param {Boolean} preservePanel Overrides the config preservePanel option
45744      * @return {Roo.ContentPanel} The panel that was removed
45745      */
45746     remove : function(panel, preservePanel){
45747         panel = this.getPanel(panel);
45748         if(!panel){
45749             return null;
45750         }
45751         var e = {};
45752         this.fireEvent("beforeremove", this, panel, e);
45753         if(e.cancel === true){
45754             return null;
45755         }
45756         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
45757         var panelId = panel.getId();
45758         this.panels.removeKey(panelId);
45759         if(preservePanel){
45760             document.body.appendChild(panel.getEl().dom);
45761         }
45762         if(this.tabs){
45763             this.tabs.removeTab(panel.getEl().id);
45764         }else if (!preservePanel){
45765             this.bodyEl.dom.removeChild(panel.getEl().dom);
45766         }
45767         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
45768             var p = this.panels.first();
45769             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
45770             tempEl.appendChild(p.getEl().dom);
45771             this.bodyEl.update("");
45772             this.bodyEl.dom.appendChild(p.getEl().dom);
45773             tempEl = null;
45774             this.updateTitle(p.getTitle());
45775             this.tabs = null;
45776             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
45777             this.setActivePanel(p);
45778         }
45779         panel.setRegion(null);
45780         if(this.activePanel == panel){
45781             this.activePanel = null;
45782         }
45783         if(this.config.autoDestroy !== false && preservePanel !== true){
45784             try{panel.destroy();}catch(e){}
45785         }
45786         this.fireEvent("panelremoved", this, panel);
45787         return panel;
45788     },
45789
45790     /**
45791      * Returns the TabPanel component used by this region
45792      * @return {Roo.TabPanel}
45793      */
45794     getTabs : function(){
45795         return this.tabs;
45796     },
45797
45798     createTool : function(parentEl, className){
45799         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
45800             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
45801         btn.addClassOnOver("x-layout-tools-button-over");
45802         return btn;
45803     }
45804 });/*
45805  * Based on:
45806  * Ext JS Library 1.1.1
45807  * Copyright(c) 2006-2007, Ext JS, LLC.
45808  *
45809  * Originally Released Under LGPL - original licence link has changed is not relivant.
45810  *
45811  * Fork - LGPL
45812  * <script type="text/javascript">
45813  */
45814  
45815
45816
45817 /**
45818  * @class Roo.SplitLayoutRegion
45819  * @extends Roo.LayoutRegion
45820  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
45821  */
45822 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
45823     this.cursor = cursor;
45824     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
45825 };
45826
45827 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
45828     splitTip : "Drag to resize.",
45829     collapsibleSplitTip : "Drag to resize. Double click to hide.",
45830     useSplitTips : false,
45831
45832     applyConfig : function(config){
45833         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
45834         if(config.split){
45835             if(!this.split){
45836                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
45837                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
45838                 /** The SplitBar for this region 
45839                 * @type Roo.SplitBar */
45840                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
45841                 this.split.on("moved", this.onSplitMove, this);
45842                 this.split.useShim = config.useShim === true;
45843                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
45844                 if(this.useSplitTips){
45845                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
45846                 }
45847                 if(config.collapsible){
45848                     this.split.el.on("dblclick", this.collapse,  this);
45849                 }
45850             }
45851             if(typeof config.minSize != "undefined"){
45852                 this.split.minSize = config.minSize;
45853             }
45854             if(typeof config.maxSize != "undefined"){
45855                 this.split.maxSize = config.maxSize;
45856             }
45857             if(config.hideWhenEmpty || config.hidden || config.collapsed){
45858                 this.hideSplitter();
45859             }
45860         }
45861     },
45862
45863     getHMaxSize : function(){
45864          var cmax = this.config.maxSize || 10000;
45865          var center = this.mgr.getRegion("center");
45866          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
45867     },
45868
45869     getVMaxSize : function(){
45870          var cmax = this.config.maxSize || 10000;
45871          var center = this.mgr.getRegion("center");
45872          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
45873     },
45874
45875     onSplitMove : function(split, newSize){
45876         this.fireEvent("resized", this, newSize);
45877     },
45878     
45879     /** 
45880      * Returns the {@link Roo.SplitBar} for this region.
45881      * @return {Roo.SplitBar}
45882      */
45883     getSplitBar : function(){
45884         return this.split;
45885     },
45886     
45887     hide : function(){
45888         this.hideSplitter();
45889         Roo.SplitLayoutRegion.superclass.hide.call(this);
45890     },
45891
45892     hideSplitter : function(){
45893         if(this.split){
45894             this.split.el.setLocation(-2000,-2000);
45895             this.split.el.hide();
45896         }
45897     },
45898
45899     show : function(){
45900         if(this.split){
45901             this.split.el.show();
45902         }
45903         Roo.SplitLayoutRegion.superclass.show.call(this);
45904     },
45905     
45906     beforeSlide: function(){
45907         if(Roo.isGecko){// firefox overflow auto bug workaround
45908             this.bodyEl.clip();
45909             if(this.tabs) this.tabs.bodyEl.clip();
45910             if(this.activePanel){
45911                 this.activePanel.getEl().clip();
45912                 
45913                 if(this.activePanel.beforeSlide){
45914                     this.activePanel.beforeSlide();
45915                 }
45916             }
45917         }
45918     },
45919     
45920     afterSlide : function(){
45921         if(Roo.isGecko){// firefox overflow auto bug workaround
45922             this.bodyEl.unclip();
45923             if(this.tabs) this.tabs.bodyEl.unclip();
45924             if(this.activePanel){
45925                 this.activePanel.getEl().unclip();
45926                 if(this.activePanel.afterSlide){
45927                     this.activePanel.afterSlide();
45928                 }
45929             }
45930         }
45931     },
45932
45933     initAutoHide : function(){
45934         if(this.autoHide !== false){
45935             if(!this.autoHideHd){
45936                 var st = new Roo.util.DelayedTask(this.slideIn, this);
45937                 this.autoHideHd = {
45938                     "mouseout": function(e){
45939                         if(!e.within(this.el, true)){
45940                             st.delay(500);
45941                         }
45942                     },
45943                     "mouseover" : function(e){
45944                         st.cancel();
45945                     },
45946                     scope : this
45947                 };
45948             }
45949             this.el.on(this.autoHideHd);
45950         }
45951     },
45952
45953     clearAutoHide : function(){
45954         if(this.autoHide !== false){
45955             this.el.un("mouseout", this.autoHideHd.mouseout);
45956             this.el.un("mouseover", this.autoHideHd.mouseover);
45957         }
45958     },
45959
45960     clearMonitor : function(){
45961         Roo.get(document).un("click", this.slideInIf, this);
45962     },
45963
45964     // these names are backwards but not changed for compat
45965     slideOut : function(){
45966         if(this.isSlid || this.el.hasActiveFx()){
45967             return;
45968         }
45969         this.isSlid = true;
45970         if(this.collapseBtn){
45971             this.collapseBtn.hide();
45972         }
45973         this.closeBtnState = this.closeBtn.getStyle('display');
45974         this.closeBtn.hide();
45975         if(this.stickBtn){
45976             this.stickBtn.show();
45977         }
45978         this.el.show();
45979         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
45980         this.beforeSlide();
45981         this.el.setStyle("z-index", 10001);
45982         this.el.slideIn(this.getSlideAnchor(), {
45983             callback: function(){
45984                 this.afterSlide();
45985                 this.initAutoHide();
45986                 Roo.get(document).on("click", this.slideInIf, this);
45987                 this.fireEvent("slideshow", this);
45988             },
45989             scope: this,
45990             block: true
45991         });
45992     },
45993
45994     afterSlideIn : function(){
45995         this.clearAutoHide();
45996         this.isSlid = false;
45997         this.clearMonitor();
45998         this.el.setStyle("z-index", "");
45999         if(this.collapseBtn){
46000             this.collapseBtn.show();
46001         }
46002         this.closeBtn.setStyle('display', this.closeBtnState);
46003         if(this.stickBtn){
46004             this.stickBtn.hide();
46005         }
46006         this.fireEvent("slidehide", this);
46007     },
46008
46009     slideIn : function(cb){
46010         if(!this.isSlid || this.el.hasActiveFx()){
46011             Roo.callback(cb);
46012             return;
46013         }
46014         this.isSlid = false;
46015         this.beforeSlide();
46016         this.el.slideOut(this.getSlideAnchor(), {
46017             callback: function(){
46018                 this.el.setLeftTop(-10000, -10000);
46019                 this.afterSlide();
46020                 this.afterSlideIn();
46021                 Roo.callback(cb);
46022             },
46023             scope: this,
46024             block: true
46025         });
46026     },
46027     
46028     slideInIf : function(e){
46029         if(!e.within(this.el)){
46030             this.slideIn();
46031         }
46032     },
46033
46034     animateCollapse : function(){
46035         this.beforeSlide();
46036         this.el.setStyle("z-index", 20000);
46037         var anchor = this.getSlideAnchor();
46038         this.el.slideOut(anchor, {
46039             callback : function(){
46040                 this.el.setStyle("z-index", "");
46041                 this.collapsedEl.slideIn(anchor, {duration:.3});
46042                 this.afterSlide();
46043                 this.el.setLocation(-10000,-10000);
46044                 this.el.hide();
46045                 this.fireEvent("collapsed", this);
46046             },
46047             scope: this,
46048             block: true
46049         });
46050     },
46051
46052     animateExpand : function(){
46053         this.beforeSlide();
46054         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
46055         this.el.setStyle("z-index", 20000);
46056         this.collapsedEl.hide({
46057             duration:.1
46058         });
46059         this.el.slideIn(this.getSlideAnchor(), {
46060             callback : function(){
46061                 this.el.setStyle("z-index", "");
46062                 this.afterSlide();
46063                 if(this.split){
46064                     this.split.el.show();
46065                 }
46066                 this.fireEvent("invalidated", this);
46067                 this.fireEvent("expanded", this);
46068             },
46069             scope: this,
46070             block: true
46071         });
46072     },
46073
46074     anchors : {
46075         "west" : "left",
46076         "east" : "right",
46077         "north" : "top",
46078         "south" : "bottom"
46079     },
46080
46081     sanchors : {
46082         "west" : "l",
46083         "east" : "r",
46084         "north" : "t",
46085         "south" : "b"
46086     },
46087
46088     canchors : {
46089         "west" : "tl-tr",
46090         "east" : "tr-tl",
46091         "north" : "tl-bl",
46092         "south" : "bl-tl"
46093     },
46094
46095     getAnchor : function(){
46096         return this.anchors[this.position];
46097     },
46098
46099     getCollapseAnchor : function(){
46100         return this.canchors[this.position];
46101     },
46102
46103     getSlideAnchor : function(){
46104         return this.sanchors[this.position];
46105     },
46106
46107     getAlignAdj : function(){
46108         var cm = this.cmargins;
46109         switch(this.position){
46110             case "west":
46111                 return [0, 0];
46112             break;
46113             case "east":
46114                 return [0, 0];
46115             break;
46116             case "north":
46117                 return [0, 0];
46118             break;
46119             case "south":
46120                 return [0, 0];
46121             break;
46122         }
46123     },
46124
46125     getExpandAdj : function(){
46126         var c = this.collapsedEl, cm = this.cmargins;
46127         switch(this.position){
46128             case "west":
46129                 return [-(cm.right+c.getWidth()+cm.left), 0];
46130             break;
46131             case "east":
46132                 return [cm.right+c.getWidth()+cm.left, 0];
46133             break;
46134             case "north":
46135                 return [0, -(cm.top+cm.bottom+c.getHeight())];
46136             break;
46137             case "south":
46138                 return [0, cm.top+cm.bottom+c.getHeight()];
46139             break;
46140         }
46141     }
46142 });/*
46143  * Based on:
46144  * Ext JS Library 1.1.1
46145  * Copyright(c) 2006-2007, Ext JS, LLC.
46146  *
46147  * Originally Released Under LGPL - original licence link has changed is not relivant.
46148  *
46149  * Fork - LGPL
46150  * <script type="text/javascript">
46151  */
46152 /*
46153  * These classes are private internal classes
46154  */
46155 Roo.CenterLayoutRegion = function(mgr, config){
46156     Roo.LayoutRegion.call(this, mgr, config, "center");
46157     this.visible = true;
46158     this.minWidth = config.minWidth || 20;
46159     this.minHeight = config.minHeight || 20;
46160 };
46161
46162 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
46163     hide : function(){
46164         // center panel can't be hidden
46165     },
46166     
46167     show : function(){
46168         // center panel can't be hidden
46169     },
46170     
46171     getMinWidth: function(){
46172         return this.minWidth;
46173     },
46174     
46175     getMinHeight: function(){
46176         return this.minHeight;
46177     }
46178 });
46179
46180
46181 Roo.NorthLayoutRegion = function(mgr, config){
46182     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
46183     if(this.split){
46184         this.split.placement = Roo.SplitBar.TOP;
46185         this.split.orientation = Roo.SplitBar.VERTICAL;
46186         this.split.el.addClass("x-layout-split-v");
46187     }
46188     var size = config.initialSize || config.height;
46189     if(typeof size != "undefined"){
46190         this.el.setHeight(size);
46191     }
46192 };
46193 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
46194     orientation: Roo.SplitBar.VERTICAL,
46195     getBox : function(){
46196         if(this.collapsed){
46197             return this.collapsedEl.getBox();
46198         }
46199         var box = this.el.getBox();
46200         if(this.split){
46201             box.height += this.split.el.getHeight();
46202         }
46203         return box;
46204     },
46205     
46206     updateBox : function(box){
46207         if(this.split && !this.collapsed){
46208             box.height -= this.split.el.getHeight();
46209             this.split.el.setLeft(box.x);
46210             this.split.el.setTop(box.y+box.height);
46211             this.split.el.setWidth(box.width);
46212         }
46213         if(this.collapsed){
46214             this.updateBody(box.width, null);
46215         }
46216         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46217     }
46218 });
46219
46220 Roo.SouthLayoutRegion = function(mgr, config){
46221     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
46222     if(this.split){
46223         this.split.placement = Roo.SplitBar.BOTTOM;
46224         this.split.orientation = Roo.SplitBar.VERTICAL;
46225         this.split.el.addClass("x-layout-split-v");
46226     }
46227     var size = config.initialSize || config.height;
46228     if(typeof size != "undefined"){
46229         this.el.setHeight(size);
46230     }
46231 };
46232 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
46233     orientation: Roo.SplitBar.VERTICAL,
46234     getBox : function(){
46235         if(this.collapsed){
46236             return this.collapsedEl.getBox();
46237         }
46238         var box = this.el.getBox();
46239         if(this.split){
46240             var sh = this.split.el.getHeight();
46241             box.height += sh;
46242             box.y -= sh;
46243         }
46244         return box;
46245     },
46246     
46247     updateBox : function(box){
46248         if(this.split && !this.collapsed){
46249             var sh = this.split.el.getHeight();
46250             box.height -= sh;
46251             box.y += sh;
46252             this.split.el.setLeft(box.x);
46253             this.split.el.setTop(box.y-sh);
46254             this.split.el.setWidth(box.width);
46255         }
46256         if(this.collapsed){
46257             this.updateBody(box.width, null);
46258         }
46259         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46260     }
46261 });
46262
46263 Roo.EastLayoutRegion = function(mgr, config){
46264     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
46265     if(this.split){
46266         this.split.placement = Roo.SplitBar.RIGHT;
46267         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46268         this.split.el.addClass("x-layout-split-h");
46269     }
46270     var size = config.initialSize || config.width;
46271     if(typeof size != "undefined"){
46272         this.el.setWidth(size);
46273     }
46274 };
46275 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
46276     orientation: Roo.SplitBar.HORIZONTAL,
46277     getBox : function(){
46278         if(this.collapsed){
46279             return this.collapsedEl.getBox();
46280         }
46281         var box = this.el.getBox();
46282         if(this.split){
46283             var sw = this.split.el.getWidth();
46284             box.width += sw;
46285             box.x -= sw;
46286         }
46287         return box;
46288     },
46289
46290     updateBox : function(box){
46291         if(this.split && !this.collapsed){
46292             var sw = this.split.el.getWidth();
46293             box.width -= sw;
46294             this.split.el.setLeft(box.x);
46295             this.split.el.setTop(box.y);
46296             this.split.el.setHeight(box.height);
46297             box.x += sw;
46298         }
46299         if(this.collapsed){
46300             this.updateBody(null, box.height);
46301         }
46302         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46303     }
46304 });
46305
46306 Roo.WestLayoutRegion = function(mgr, config){
46307     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
46308     if(this.split){
46309         this.split.placement = Roo.SplitBar.LEFT;
46310         this.split.orientation = Roo.SplitBar.HORIZONTAL;
46311         this.split.el.addClass("x-layout-split-h");
46312     }
46313     var size = config.initialSize || config.width;
46314     if(typeof size != "undefined"){
46315         this.el.setWidth(size);
46316     }
46317 };
46318 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
46319     orientation: Roo.SplitBar.HORIZONTAL,
46320     getBox : function(){
46321         if(this.collapsed){
46322             return this.collapsedEl.getBox();
46323         }
46324         var box = this.el.getBox();
46325         if(this.split){
46326             box.width += this.split.el.getWidth();
46327         }
46328         return box;
46329     },
46330     
46331     updateBox : function(box){
46332         if(this.split && !this.collapsed){
46333             var sw = this.split.el.getWidth();
46334             box.width -= sw;
46335             this.split.el.setLeft(box.x+box.width);
46336             this.split.el.setTop(box.y);
46337             this.split.el.setHeight(box.height);
46338         }
46339         if(this.collapsed){
46340             this.updateBody(null, box.height);
46341         }
46342         Roo.LayoutRegion.prototype.updateBox.call(this, box);
46343     }
46344 });
46345 /*
46346  * Based on:
46347  * Ext JS Library 1.1.1
46348  * Copyright(c) 2006-2007, Ext JS, LLC.
46349  *
46350  * Originally Released Under LGPL - original licence link has changed is not relivant.
46351  *
46352  * Fork - LGPL
46353  * <script type="text/javascript">
46354  */
46355  
46356  
46357 /*
46358  * Private internal class for reading and applying state
46359  */
46360 Roo.LayoutStateManager = function(layout){
46361      // default empty state
46362      this.state = {
46363         north: {},
46364         south: {},
46365         east: {},
46366         west: {}       
46367     };
46368 };
46369
46370 Roo.LayoutStateManager.prototype = {
46371     init : function(layout, provider){
46372         this.provider = provider;
46373         var state = provider.get(layout.id+"-layout-state");
46374         if(state){
46375             var wasUpdating = layout.isUpdating();
46376             if(!wasUpdating){
46377                 layout.beginUpdate();
46378             }
46379             for(var key in state){
46380                 if(typeof state[key] != "function"){
46381                     var rstate = state[key];
46382                     var r = layout.getRegion(key);
46383                     if(r && rstate){
46384                         if(rstate.size){
46385                             r.resizeTo(rstate.size);
46386                         }
46387                         if(rstate.collapsed == true){
46388                             r.collapse(true);
46389                         }else{
46390                             r.expand(null, true);
46391                         }
46392                     }
46393                 }
46394             }
46395             if(!wasUpdating){
46396                 layout.endUpdate();
46397             }
46398             this.state = state; 
46399         }
46400         this.layout = layout;
46401         layout.on("regionresized", this.onRegionResized, this);
46402         layout.on("regioncollapsed", this.onRegionCollapsed, this);
46403         layout.on("regionexpanded", this.onRegionExpanded, this);
46404     },
46405     
46406     storeState : function(){
46407         this.provider.set(this.layout.id+"-layout-state", this.state);
46408     },
46409     
46410     onRegionResized : function(region, newSize){
46411         this.state[region.getPosition()].size = newSize;
46412         this.storeState();
46413     },
46414     
46415     onRegionCollapsed : function(region){
46416         this.state[region.getPosition()].collapsed = true;
46417         this.storeState();
46418     },
46419     
46420     onRegionExpanded : function(region){
46421         this.state[region.getPosition()].collapsed = false;
46422         this.storeState();
46423     }
46424 };/*
46425  * Based on:
46426  * Ext JS Library 1.1.1
46427  * Copyright(c) 2006-2007, Ext JS, LLC.
46428  *
46429  * Originally Released Under LGPL - original licence link has changed is not relivant.
46430  *
46431  * Fork - LGPL
46432  * <script type="text/javascript">
46433  */
46434 /**
46435  * @class Roo.ContentPanel
46436  * @extends Roo.util.Observable
46437  * A basic ContentPanel element.
46438  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
46439  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
46440  * @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
46441  * @cfg {Boolean}   closable      True if the panel can be closed/removed
46442  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
46443  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
46444  * @cfg {Toolbar}   toolbar       A toolbar for this panel
46445  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
46446  * @cfg {String} title          The title for this panel
46447  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
46448  * @cfg {String} url            Calls {@link #setUrl} with this value
46449  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
46450  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
46451  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
46452  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
46453
46454  * @constructor
46455  * Create a new ContentPanel.
46456  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
46457  * @param {String/Object} config A string to set only the title or a config object
46458  * @param {String} content (optional) Set the HTML content for this panel
46459  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
46460  */
46461 Roo.ContentPanel = function(el, config, content){
46462     
46463      
46464     /*
46465     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
46466         config = el;
46467         el = Roo.id();
46468     }
46469     if (config && config.parentLayout) { 
46470         el = config.parentLayout.el.createChild(); 
46471     }
46472     */
46473     if(el.autoCreate){ // xtype is available if this is called from factory
46474         config = el;
46475         el = Roo.id();
46476     }
46477     this.el = Roo.get(el);
46478     if(!this.el && config && config.autoCreate){
46479         if(typeof config.autoCreate == "object"){
46480             if(!config.autoCreate.id){
46481                 config.autoCreate.id = config.id||el;
46482             }
46483             this.el = Roo.DomHelper.append(document.body,
46484                         config.autoCreate, true);
46485         }else{
46486             this.el = Roo.DomHelper.append(document.body,
46487                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
46488         }
46489     }
46490     this.closable = false;
46491     this.loaded = false;
46492     this.active = false;
46493     if(typeof config == "string"){
46494         this.title = config;
46495     }else{
46496         Roo.apply(this, config);
46497     }
46498     
46499     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
46500         this.wrapEl = this.el.wrap();
46501         this.toolbar.container = this.el.insertSibling(false, 'before');
46502         this.toolbar = new Roo.Toolbar(this.toolbar);
46503     }
46504     
46505     
46506     
46507     if(this.resizeEl){
46508         this.resizeEl = Roo.get(this.resizeEl, true);
46509     }else{
46510         this.resizeEl = this.el;
46511     }
46512     this.addEvents({
46513         /**
46514          * @event activate
46515          * Fires when this panel is activated. 
46516          * @param {Roo.ContentPanel} this
46517          */
46518         "activate" : true,
46519         /**
46520          * @event deactivate
46521          * Fires when this panel is activated. 
46522          * @param {Roo.ContentPanel} this
46523          */
46524         "deactivate" : true,
46525
46526         /**
46527          * @event resize
46528          * Fires when this panel is resized if fitToFrame is true.
46529          * @param {Roo.ContentPanel} this
46530          * @param {Number} width The width after any component adjustments
46531          * @param {Number} height The height after any component adjustments
46532          */
46533         "resize" : true,
46534         
46535          /**
46536          * @event render
46537          * Fires when this tab is created
46538          * @param {Roo.ContentPanel} this
46539          */
46540         "render" : true
46541         
46542         
46543         
46544     });
46545     if(this.autoScroll){
46546         this.resizeEl.setStyle("overflow", "auto");
46547     } else {
46548         // fix randome scrolling
46549         this.el.on('scroll', function() {
46550             Roo.log('fix random scolling');
46551             this.scrollTo('top',0); 
46552         });
46553     }
46554     content = content || this.content;
46555     if(content){
46556         this.setContent(content);
46557     }
46558     if(config && config.url){
46559         this.setUrl(this.url, this.params, this.loadOnce);
46560     }
46561     
46562     
46563     
46564     Roo.ContentPanel.superclass.constructor.call(this);
46565     
46566     this.fireEvent('render', this);
46567 };
46568
46569 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
46570     tabTip:'',
46571     setRegion : function(region){
46572         this.region = region;
46573         if(region){
46574            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
46575         }else{
46576            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
46577         } 
46578     },
46579     
46580     /**
46581      * Returns the toolbar for this Panel if one was configured. 
46582      * @return {Roo.Toolbar} 
46583      */
46584     getToolbar : function(){
46585         return this.toolbar;
46586     },
46587     
46588     setActiveState : function(active){
46589         this.active = active;
46590         if(!active){
46591             this.fireEvent("deactivate", this);
46592         }else{
46593             this.fireEvent("activate", this);
46594         }
46595     },
46596     /**
46597      * Updates this panel's element
46598      * @param {String} content The new content
46599      * @param {Boolean} loadScripts (optional) true to look for and process scripts
46600     */
46601     setContent : function(content, loadScripts){
46602         this.el.update(content, loadScripts);
46603     },
46604
46605     ignoreResize : function(w, h){
46606         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
46607             return true;
46608         }else{
46609             this.lastSize = {width: w, height: h};
46610             return false;
46611         }
46612     },
46613     /**
46614      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
46615      * @return {Roo.UpdateManager} The UpdateManager
46616      */
46617     getUpdateManager : function(){
46618         return this.el.getUpdateManager();
46619     },
46620      /**
46621      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
46622      * @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:
46623 <pre><code>
46624 panel.load({
46625     url: "your-url.php",
46626     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
46627     callback: yourFunction,
46628     scope: yourObject, //(optional scope)
46629     discardUrl: false,
46630     nocache: false,
46631     text: "Loading...",
46632     timeout: 30,
46633     scripts: false
46634 });
46635 </code></pre>
46636      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
46637      * 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.
46638      * @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}
46639      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
46640      * @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.
46641      * @return {Roo.ContentPanel} this
46642      */
46643     load : function(){
46644         var um = this.el.getUpdateManager();
46645         um.update.apply(um, arguments);
46646         return this;
46647     },
46648
46649
46650     /**
46651      * 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.
46652      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
46653      * @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)
46654      * @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)
46655      * @return {Roo.UpdateManager} The UpdateManager
46656      */
46657     setUrl : function(url, params, loadOnce){
46658         if(this.refreshDelegate){
46659             this.removeListener("activate", this.refreshDelegate);
46660         }
46661         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46662         this.on("activate", this.refreshDelegate);
46663         return this.el.getUpdateManager();
46664     },
46665     
46666     _handleRefresh : function(url, params, loadOnce){
46667         if(!loadOnce || !this.loaded){
46668             var updater = this.el.getUpdateManager();
46669             updater.update(url, params, this._setLoaded.createDelegate(this));
46670         }
46671     },
46672     
46673     _setLoaded : function(){
46674         this.loaded = true;
46675     }, 
46676     
46677     /**
46678      * Returns this panel's id
46679      * @return {String} 
46680      */
46681     getId : function(){
46682         return this.el.id;
46683     },
46684     
46685     /** 
46686      * Returns this panel's element - used by regiosn to add.
46687      * @return {Roo.Element} 
46688      */
46689     getEl : function(){
46690         return this.wrapEl || this.el;
46691     },
46692     
46693     adjustForComponents : function(width, height){
46694         if(this.resizeEl != this.el){
46695             width -= this.el.getFrameWidth('lr');
46696             height -= this.el.getFrameWidth('tb');
46697         }
46698         if(this.toolbar){
46699             var te = this.toolbar.getEl();
46700             height -= te.getHeight();
46701             te.setWidth(width);
46702         }
46703         if(this.adjustments){
46704             width += this.adjustments[0];
46705             height += this.adjustments[1];
46706         }
46707         return {"width": width, "height": height};
46708     },
46709     
46710     setSize : function(width, height){
46711         if(this.fitToFrame && !this.ignoreResize(width, height)){
46712             if(this.fitContainer && this.resizeEl != this.el){
46713                 this.el.setSize(width, height);
46714             }
46715             var size = this.adjustForComponents(width, height);
46716             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
46717             this.fireEvent('resize', this, size.width, size.height);
46718         }
46719     },
46720     
46721     /**
46722      * Returns this panel's title
46723      * @return {String} 
46724      */
46725     getTitle : function(){
46726         return this.title;
46727     },
46728     
46729     /**
46730      * Set this panel's title
46731      * @param {String} title
46732      */
46733     setTitle : function(title){
46734         this.title = title;
46735         if(this.region){
46736             this.region.updatePanelTitle(this, title);
46737         }
46738     },
46739     
46740     /**
46741      * Returns true is this panel was configured to be closable
46742      * @return {Boolean} 
46743      */
46744     isClosable : function(){
46745         return this.closable;
46746     },
46747     
46748     beforeSlide : function(){
46749         this.el.clip();
46750         this.resizeEl.clip();
46751     },
46752     
46753     afterSlide : function(){
46754         this.el.unclip();
46755         this.resizeEl.unclip();
46756     },
46757     
46758     /**
46759      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
46760      *   Will fail silently if the {@link #setUrl} method has not been called.
46761      *   This does not activate the panel, just updates its content.
46762      */
46763     refresh : function(){
46764         if(this.refreshDelegate){
46765            this.loaded = false;
46766            this.refreshDelegate();
46767         }
46768     },
46769     
46770     /**
46771      * Destroys this panel
46772      */
46773     destroy : function(){
46774         this.el.removeAllListeners();
46775         var tempEl = document.createElement("span");
46776         tempEl.appendChild(this.el.dom);
46777         tempEl.innerHTML = "";
46778         this.el.remove();
46779         this.el = null;
46780     },
46781     
46782     /**
46783      * form - if the content panel contains a form - this is a reference to it.
46784      * @type {Roo.form.Form}
46785      */
46786     form : false,
46787     /**
46788      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
46789      *    This contains a reference to it.
46790      * @type {Roo.View}
46791      */
46792     view : false,
46793     
46794       /**
46795      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
46796      * <pre><code>
46797
46798 layout.addxtype({
46799        xtype : 'Form',
46800        items: [ .... ]
46801    }
46802 );
46803
46804 </code></pre>
46805      * @param {Object} cfg Xtype definition of item to add.
46806      */
46807     
46808     addxtype : function(cfg) {
46809         // add form..
46810         if (cfg.xtype.match(/^Form$/)) {
46811             var el = this.el.createChild();
46812
46813             this.form = new  Roo.form.Form(cfg);
46814             
46815             
46816             if ( this.form.allItems.length) this.form.render(el.dom);
46817             return this.form;
46818         }
46819         // should only have one of theses..
46820         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
46821             // views..
46822             cfg.el = this.el.appendChild(document.createElement("div"));
46823             // factory?
46824             
46825             var ret = new Roo.factory(cfg);
46826             ret.render && ret.render(false, ''); // render blank..
46827             this.view = ret;
46828             return ret;
46829         }
46830         return false;
46831     }
46832 });
46833
46834 /**
46835  * @class Roo.GridPanel
46836  * @extends Roo.ContentPanel
46837  * @constructor
46838  * Create a new GridPanel.
46839  * @param {Roo.grid.Grid} grid The grid for this panel
46840  * @param {String/Object} config A string to set only the panel's title, or a config object
46841  */
46842 Roo.GridPanel = function(grid, config){
46843     
46844   
46845     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
46846         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
46847         
46848     this.wrapper.dom.appendChild(grid.getGridEl().dom);
46849     
46850     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
46851     
46852     if(this.toolbar){
46853         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
46854     }
46855     // xtype created footer. - not sure if will work as we normally have to render first..
46856     if (this.footer && !this.footer.el && this.footer.xtype) {
46857         
46858         this.footer.container = this.grid.getView().getFooterPanel(true);
46859         this.footer.dataSource = this.grid.dataSource;
46860         this.footer = Roo.factory(this.footer, Roo);
46861         
46862     }
46863     
46864     grid.monitorWindowResize = false; // turn off autosizing
46865     grid.autoHeight = false;
46866     grid.autoWidth = false;
46867     this.grid = grid;
46868     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
46869 };
46870
46871 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
46872     getId : function(){
46873         return this.grid.id;
46874     },
46875     
46876     /**
46877      * Returns the grid for this panel
46878      * @return {Roo.grid.Grid} 
46879      */
46880     getGrid : function(){
46881         return this.grid;    
46882     },
46883     
46884     setSize : function(width, height){
46885         if(!this.ignoreResize(width, height)){
46886             var grid = this.grid;
46887             var size = this.adjustForComponents(width, height);
46888             grid.getGridEl().setSize(size.width, size.height);
46889             grid.autoSize();
46890         }
46891     },
46892     
46893     beforeSlide : function(){
46894         this.grid.getView().scroller.clip();
46895     },
46896     
46897     afterSlide : function(){
46898         this.grid.getView().scroller.unclip();
46899     },
46900     
46901     destroy : function(){
46902         this.grid.destroy();
46903         delete this.grid;
46904         Roo.GridPanel.superclass.destroy.call(this); 
46905     }
46906 });
46907
46908
46909 /**
46910  * @class Roo.NestedLayoutPanel
46911  * @extends Roo.ContentPanel
46912  * @constructor
46913  * Create a new NestedLayoutPanel.
46914  * 
46915  * 
46916  * @param {Roo.BorderLayout} layout The layout for this panel
46917  * @param {String/Object} config A string to set only the title or a config object
46918  */
46919 Roo.NestedLayoutPanel = function(layout, config)
46920 {
46921     // construct with only one argument..
46922     /* FIXME - implement nicer consturctors
46923     if (layout.layout) {
46924         config = layout;
46925         layout = config.layout;
46926         delete config.layout;
46927     }
46928     if (layout.xtype && !layout.getEl) {
46929         // then layout needs constructing..
46930         layout = Roo.factory(layout, Roo);
46931     }
46932     */
46933     
46934     
46935     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
46936     
46937     layout.monitorWindowResize = false; // turn off autosizing
46938     this.layout = layout;
46939     this.layout.getEl().addClass("x-layout-nested-layout");
46940     
46941     
46942     
46943     
46944 };
46945
46946 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
46947
46948     setSize : function(width, height){
46949         if(!this.ignoreResize(width, height)){
46950             var size = this.adjustForComponents(width, height);
46951             var el = this.layout.getEl();
46952             el.setSize(size.width, size.height);
46953             var touch = el.dom.offsetWidth;
46954             this.layout.layout();
46955             // ie requires a double layout on the first pass
46956             if(Roo.isIE && !this.initialized){
46957                 this.initialized = true;
46958                 this.layout.layout();
46959             }
46960         }
46961     },
46962     
46963     // activate all subpanels if not currently active..
46964     
46965     setActiveState : function(active){
46966         this.active = active;
46967         if(!active){
46968             this.fireEvent("deactivate", this);
46969             return;
46970         }
46971         
46972         this.fireEvent("activate", this);
46973         // not sure if this should happen before or after..
46974         if (!this.layout) {
46975             return; // should not happen..
46976         }
46977         var reg = false;
46978         for (var r in this.layout.regions) {
46979             reg = this.layout.getRegion(r);
46980             if (reg.getActivePanel()) {
46981                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
46982                 reg.setActivePanel(reg.getActivePanel());
46983                 continue;
46984             }
46985             if (!reg.panels.length) {
46986                 continue;
46987             }
46988             reg.showPanel(reg.getPanel(0));
46989         }
46990         
46991         
46992         
46993         
46994     },
46995     
46996     /**
46997      * Returns the nested BorderLayout for this panel
46998      * @return {Roo.BorderLayout} 
46999      */
47000     getLayout : function(){
47001         return this.layout;
47002     },
47003     
47004      /**
47005      * Adds a xtype elements to the layout of the nested panel
47006      * <pre><code>
47007
47008 panel.addxtype({
47009        xtype : 'ContentPanel',
47010        region: 'west',
47011        items: [ .... ]
47012    }
47013 );
47014
47015 panel.addxtype({
47016         xtype : 'NestedLayoutPanel',
47017         region: 'west',
47018         layout: {
47019            center: { },
47020            west: { }   
47021         },
47022         items : [ ... list of content panels or nested layout panels.. ]
47023    }
47024 );
47025 </code></pre>
47026      * @param {Object} cfg Xtype definition of item to add.
47027      */
47028     addxtype : function(cfg) {
47029         return this.layout.addxtype(cfg);
47030     
47031     }
47032 });
47033
47034 Roo.ScrollPanel = function(el, config, content){
47035     config = config || {};
47036     config.fitToFrame = true;
47037     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
47038     
47039     this.el.dom.style.overflow = "hidden";
47040     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
47041     this.el.removeClass("x-layout-inactive-content");
47042     this.el.on("mousewheel", this.onWheel, this);
47043
47044     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
47045     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
47046     up.unselectable(); down.unselectable();
47047     up.on("click", this.scrollUp, this);
47048     down.on("click", this.scrollDown, this);
47049     up.addClassOnOver("x-scroller-btn-over");
47050     down.addClassOnOver("x-scroller-btn-over");
47051     up.addClassOnClick("x-scroller-btn-click");
47052     down.addClassOnClick("x-scroller-btn-click");
47053     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
47054
47055     this.resizeEl = this.el;
47056     this.el = wrap; this.up = up; this.down = down;
47057 };
47058
47059 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
47060     increment : 100,
47061     wheelIncrement : 5,
47062     scrollUp : function(){
47063         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
47064     },
47065
47066     scrollDown : function(){
47067         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
47068     },
47069
47070     afterScroll : function(){
47071         var el = this.resizeEl;
47072         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
47073         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47074         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
47075     },
47076
47077     setSize : function(){
47078         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
47079         this.afterScroll();
47080     },
47081
47082     onWheel : function(e){
47083         var d = e.getWheelDelta();
47084         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
47085         this.afterScroll();
47086         e.stopEvent();
47087     },
47088
47089     setContent : function(content, loadScripts){
47090         this.resizeEl.update(content, loadScripts);
47091     }
47092
47093 });
47094
47095
47096
47097
47098
47099
47100
47101
47102
47103 /**
47104  * @class Roo.TreePanel
47105  * @extends Roo.ContentPanel
47106  * @constructor
47107  * Create a new TreePanel. - defaults to fit/scoll contents.
47108  * @param {String/Object} config A string to set only the panel's title, or a config object
47109  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
47110  */
47111 Roo.TreePanel = function(config){
47112     var el = config.el;
47113     var tree = config.tree;
47114     delete config.tree; 
47115     delete config.el; // hopefull!
47116     
47117     // wrapper for IE7 strict & safari scroll issue
47118     
47119     var treeEl = el.createChild();
47120     config.resizeEl = treeEl;
47121     
47122     
47123     
47124     Roo.TreePanel.superclass.constructor.call(this, el, config);
47125  
47126  
47127     this.tree = new Roo.tree.TreePanel(treeEl , tree);
47128     //console.log(tree);
47129     this.on('activate', function()
47130     {
47131         if (this.tree.rendered) {
47132             return;
47133         }
47134         //console.log('render tree');
47135         this.tree.render();
47136     });
47137     
47138     this.on('resize',  function (cp, w, h) {
47139             this.tree.innerCt.setWidth(w);
47140             this.tree.innerCt.setHeight(h);
47141             this.tree.innerCt.setStyle('overflow-y', 'auto');
47142     });
47143
47144         
47145     
47146 };
47147
47148 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
47149     fitToFrame : true,
47150     autoScroll : true
47151 });
47152
47153
47154
47155
47156
47157
47158
47159
47160
47161
47162
47163 /*
47164  * Based on:
47165  * Ext JS Library 1.1.1
47166  * Copyright(c) 2006-2007, Ext JS, LLC.
47167  *
47168  * Originally Released Under LGPL - original licence link has changed is not relivant.
47169  *
47170  * Fork - LGPL
47171  * <script type="text/javascript">
47172  */
47173  
47174
47175 /**
47176  * @class Roo.ReaderLayout
47177  * @extends Roo.BorderLayout
47178  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
47179  * center region containing two nested regions (a top one for a list view and one for item preview below),
47180  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
47181  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
47182  * expedites the setup of the overall layout and regions for this common application style.
47183  * Example:
47184  <pre><code>
47185 var reader = new Roo.ReaderLayout();
47186 var CP = Roo.ContentPanel;  // shortcut for adding
47187
47188 reader.beginUpdate();
47189 reader.add("north", new CP("north", "North"));
47190 reader.add("west", new CP("west", {title: "West"}));
47191 reader.add("east", new CP("east", {title: "East"}));
47192
47193 reader.regions.listView.add(new CP("listView", "List"));
47194 reader.regions.preview.add(new CP("preview", "Preview"));
47195 reader.endUpdate();
47196 </code></pre>
47197 * @constructor
47198 * Create a new ReaderLayout
47199 * @param {Object} config Configuration options
47200 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
47201 * document.body if omitted)
47202 */
47203 Roo.ReaderLayout = function(config, renderTo){
47204     var c = config || {size:{}};
47205     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
47206         north: c.north !== false ? Roo.apply({
47207             split:false,
47208             initialSize: 32,
47209             titlebar: false
47210         }, c.north) : false,
47211         west: c.west !== false ? Roo.apply({
47212             split:true,
47213             initialSize: 200,
47214             minSize: 175,
47215             maxSize: 400,
47216             titlebar: true,
47217             collapsible: true,
47218             animate: true,
47219             margins:{left:5,right:0,bottom:5,top:5},
47220             cmargins:{left:5,right:5,bottom:5,top:5}
47221         }, c.west) : false,
47222         east: c.east !== false ? Roo.apply({
47223             split:true,
47224             initialSize: 200,
47225             minSize: 175,
47226             maxSize: 400,
47227             titlebar: true,
47228             collapsible: true,
47229             animate: true,
47230             margins:{left:0,right:5,bottom:5,top:5},
47231             cmargins:{left:5,right:5,bottom:5,top:5}
47232         }, c.east) : false,
47233         center: Roo.apply({
47234             tabPosition: 'top',
47235             autoScroll:false,
47236             closeOnTab: true,
47237             titlebar:false,
47238             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
47239         }, c.center)
47240     });
47241
47242     this.el.addClass('x-reader');
47243
47244     this.beginUpdate();
47245
47246     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
47247         south: c.preview !== false ? Roo.apply({
47248             split:true,
47249             initialSize: 200,
47250             minSize: 100,
47251             autoScroll:true,
47252             collapsible:true,
47253             titlebar: true,
47254             cmargins:{top:5,left:0, right:0, bottom:0}
47255         }, c.preview) : false,
47256         center: Roo.apply({
47257             autoScroll:false,
47258             titlebar:false,
47259             minHeight:200
47260         }, c.listView)
47261     });
47262     this.add('center', new Roo.NestedLayoutPanel(inner,
47263             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
47264
47265     this.endUpdate();
47266
47267     this.regions.preview = inner.getRegion('south');
47268     this.regions.listView = inner.getRegion('center');
47269 };
47270
47271 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
47272  * Based on:
47273  * Ext JS Library 1.1.1
47274  * Copyright(c) 2006-2007, Ext JS, LLC.
47275  *
47276  * Originally Released Under LGPL - original licence link has changed is not relivant.
47277  *
47278  * Fork - LGPL
47279  * <script type="text/javascript">
47280  */
47281  
47282 /**
47283  * @class Roo.grid.Grid
47284  * @extends Roo.util.Observable
47285  * This class represents the primary interface of a component based grid control.
47286  * <br><br>Usage:<pre><code>
47287  var grid = new Roo.grid.Grid("my-container-id", {
47288      ds: myDataStore,
47289      cm: myColModel,
47290      selModel: mySelectionModel,
47291      autoSizeColumns: true,
47292      monitorWindowResize: false,
47293      trackMouseOver: true
47294  });
47295  // set any options
47296  grid.render();
47297  * </code></pre>
47298  * <b>Common Problems:</b><br/>
47299  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
47300  * element will correct this<br/>
47301  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
47302  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
47303  * are unpredictable.<br/>
47304  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
47305  * grid to calculate dimensions/offsets.<br/>
47306   * @constructor
47307  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
47308  * The container MUST have some type of size defined for the grid to fill. The container will be
47309  * automatically set to position relative if it isn't already.
47310  * @param {Object} config A config object that sets properties on this grid.
47311  */
47312 Roo.grid.Grid = function(container, config){
47313         // initialize the container
47314         this.container = Roo.get(container);
47315         this.container.update("");
47316         this.container.setStyle("overflow", "hidden");
47317     this.container.addClass('x-grid-container');
47318
47319     this.id = this.container.id;
47320
47321     Roo.apply(this, config);
47322     // check and correct shorthanded configs
47323     if(this.ds){
47324         this.dataSource = this.ds;
47325         delete this.ds;
47326     }
47327     if(this.cm){
47328         this.colModel = this.cm;
47329         delete this.cm;
47330     }
47331     if(this.sm){
47332         this.selModel = this.sm;
47333         delete this.sm;
47334     }
47335
47336     if (this.selModel) {
47337         this.selModel = Roo.factory(this.selModel, Roo.grid);
47338         this.sm = this.selModel;
47339         this.sm.xmodule = this.xmodule || false;
47340     }
47341     if (typeof(this.colModel.config) == 'undefined') {
47342         this.colModel = new Roo.grid.ColumnModel(this.colModel);
47343         this.cm = this.colModel;
47344         this.cm.xmodule = this.xmodule || false;
47345     }
47346     if (this.dataSource) {
47347         this.dataSource= Roo.factory(this.dataSource, Roo.data);
47348         this.ds = this.dataSource;
47349         this.ds.xmodule = this.xmodule || false;
47350          
47351     }
47352     
47353     
47354     
47355     if(this.width){
47356         this.container.setWidth(this.width);
47357     }
47358
47359     if(this.height){
47360         this.container.setHeight(this.height);
47361     }
47362     /** @private */
47363         this.addEvents({
47364         // raw events
47365         /**
47366          * @event click
47367          * The raw click event for the entire grid.
47368          * @param {Roo.EventObject} e
47369          */
47370         "click" : true,
47371         /**
47372          * @event dblclick
47373          * The raw dblclick event for the entire grid.
47374          * @param {Roo.EventObject} e
47375          */
47376         "dblclick" : true,
47377         /**
47378          * @event contextmenu
47379          * The raw contextmenu event for the entire grid.
47380          * @param {Roo.EventObject} e
47381          */
47382         "contextmenu" : true,
47383         /**
47384          * @event mousedown
47385          * The raw mousedown event for the entire grid.
47386          * @param {Roo.EventObject} e
47387          */
47388         "mousedown" : true,
47389         /**
47390          * @event mouseup
47391          * The raw mouseup event for the entire grid.
47392          * @param {Roo.EventObject} e
47393          */
47394         "mouseup" : true,
47395         /**
47396          * @event mouseover
47397          * The raw mouseover event for the entire grid.
47398          * @param {Roo.EventObject} e
47399          */
47400         "mouseover" : true,
47401         /**
47402          * @event mouseout
47403          * The raw mouseout event for the entire grid.
47404          * @param {Roo.EventObject} e
47405          */
47406         "mouseout" : true,
47407         /**
47408          * @event keypress
47409          * The raw keypress event for the entire grid.
47410          * @param {Roo.EventObject} e
47411          */
47412         "keypress" : true,
47413         /**
47414          * @event keydown
47415          * The raw keydown event for the entire grid.
47416          * @param {Roo.EventObject} e
47417          */
47418         "keydown" : true,
47419
47420         // custom events
47421
47422         /**
47423          * @event cellclick
47424          * Fires when a cell is clicked
47425          * @param {Grid} this
47426          * @param {Number} rowIndex
47427          * @param {Number} columnIndex
47428          * @param {Roo.EventObject} e
47429          */
47430         "cellclick" : true,
47431         /**
47432          * @event celldblclick
47433          * Fires when a cell is double clicked
47434          * @param {Grid} this
47435          * @param {Number} rowIndex
47436          * @param {Number} columnIndex
47437          * @param {Roo.EventObject} e
47438          */
47439         "celldblclick" : true,
47440         /**
47441          * @event rowclick
47442          * Fires when a row is clicked
47443          * @param {Grid} this
47444          * @param {Number} rowIndex
47445          * @param {Roo.EventObject} e
47446          */
47447         "rowclick" : true,
47448         /**
47449          * @event rowdblclick
47450          * Fires when a row is double clicked
47451          * @param {Grid} this
47452          * @param {Number} rowIndex
47453          * @param {Roo.EventObject} e
47454          */
47455         "rowdblclick" : true,
47456         /**
47457          * @event headerclick
47458          * Fires when a header is clicked
47459          * @param {Grid} this
47460          * @param {Number} columnIndex
47461          * @param {Roo.EventObject} e
47462          */
47463         "headerclick" : true,
47464         /**
47465          * @event headerdblclick
47466          * Fires when a header cell is double clicked
47467          * @param {Grid} this
47468          * @param {Number} columnIndex
47469          * @param {Roo.EventObject} e
47470          */
47471         "headerdblclick" : true,
47472         /**
47473          * @event rowcontextmenu
47474          * Fires when a row is right clicked
47475          * @param {Grid} this
47476          * @param {Number} rowIndex
47477          * @param {Roo.EventObject} e
47478          */
47479         "rowcontextmenu" : true,
47480         /**
47481          * @event cellcontextmenu
47482          * Fires when a cell is right clicked
47483          * @param {Grid} this
47484          * @param {Number} rowIndex
47485          * @param {Number} cellIndex
47486          * @param {Roo.EventObject} e
47487          */
47488          "cellcontextmenu" : true,
47489         /**
47490          * @event headercontextmenu
47491          * Fires when a header is right clicked
47492          * @param {Grid} this
47493          * @param {Number} columnIndex
47494          * @param {Roo.EventObject} e
47495          */
47496         "headercontextmenu" : true,
47497         /**
47498          * @event bodyscroll
47499          * Fires when the body element is scrolled
47500          * @param {Number} scrollLeft
47501          * @param {Number} scrollTop
47502          */
47503         "bodyscroll" : true,
47504         /**
47505          * @event columnresize
47506          * Fires when the user resizes a column
47507          * @param {Number} columnIndex
47508          * @param {Number} newSize
47509          */
47510         "columnresize" : true,
47511         /**
47512          * @event columnmove
47513          * Fires when the user moves a column
47514          * @param {Number} oldIndex
47515          * @param {Number} newIndex
47516          */
47517         "columnmove" : true,
47518         /**
47519          * @event startdrag
47520          * Fires when row(s) start being dragged
47521          * @param {Grid} this
47522          * @param {Roo.GridDD} dd The drag drop object
47523          * @param {event} e The raw browser event
47524          */
47525         "startdrag" : true,
47526         /**
47527          * @event enddrag
47528          * Fires when a drag operation is complete
47529          * @param {Grid} this
47530          * @param {Roo.GridDD} dd The drag drop object
47531          * @param {event} e The raw browser event
47532          */
47533         "enddrag" : true,
47534         /**
47535          * @event dragdrop
47536          * Fires when dragged row(s) are dropped on a valid DD target
47537          * @param {Grid} this
47538          * @param {Roo.GridDD} dd The drag drop object
47539          * @param {String} targetId The target drag drop object
47540          * @param {event} e The raw browser event
47541          */
47542         "dragdrop" : true,
47543         /**
47544          * @event dragover
47545          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
47546          * @param {Grid} this
47547          * @param {Roo.GridDD} dd The drag drop object
47548          * @param {String} targetId The target drag drop object
47549          * @param {event} e The raw browser event
47550          */
47551         "dragover" : true,
47552         /**
47553          * @event dragenter
47554          *  Fires when the dragged row(s) first cross another DD target while being dragged
47555          * @param {Grid} this
47556          * @param {Roo.GridDD} dd The drag drop object
47557          * @param {String} targetId The target drag drop object
47558          * @param {event} e The raw browser event
47559          */
47560         "dragenter" : true,
47561         /**
47562          * @event dragout
47563          * Fires when the dragged row(s) leave another DD target while being dragged
47564          * @param {Grid} this
47565          * @param {Roo.GridDD} dd The drag drop object
47566          * @param {String} targetId The target drag drop object
47567          * @param {event} e The raw browser event
47568          */
47569         "dragout" : true,
47570         /**
47571          * @event rowclass
47572          * Fires when a row is rendered, so you can change add a style to it.
47573          * @param {GridView} gridview   The grid view
47574          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
47575          */
47576         'rowclass' : true,
47577
47578         /**
47579          * @event render
47580          * Fires when the grid is rendered
47581          * @param {Grid} grid
47582          */
47583         'render' : true
47584     });
47585
47586     Roo.grid.Grid.superclass.constructor.call(this);
47587 };
47588 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
47589     
47590     /**
47591      * @cfg {String} ddGroup - drag drop group.
47592      */
47593
47594     /**
47595      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
47596      */
47597     minColumnWidth : 25,
47598
47599     /**
47600      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
47601      * <b>on initial render.</b> It is more efficient to explicitly size the columns
47602      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
47603      */
47604     autoSizeColumns : false,
47605
47606     /**
47607      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
47608      */
47609     autoSizeHeaders : true,
47610
47611     /**
47612      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
47613      */
47614     monitorWindowResize : true,
47615
47616     /**
47617      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
47618      * rows measured to get a columns size. Default is 0 (all rows).
47619      */
47620     maxRowsToMeasure : 0,
47621
47622     /**
47623      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
47624      */
47625     trackMouseOver : true,
47626
47627     /**
47628     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
47629     */
47630     
47631     /**
47632     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
47633     */
47634     enableDragDrop : false,
47635     
47636     /**
47637     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
47638     */
47639     enableColumnMove : true,
47640     
47641     /**
47642     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
47643     */
47644     enableColumnHide : true,
47645     
47646     /**
47647     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
47648     */
47649     enableRowHeightSync : false,
47650     
47651     /**
47652     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
47653     */
47654     stripeRows : true,
47655     
47656     /**
47657     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
47658     */
47659     autoHeight : false,
47660
47661     /**
47662      * @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.
47663      */
47664     autoExpandColumn : false,
47665
47666     /**
47667     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
47668     * Default is 50.
47669     */
47670     autoExpandMin : 50,
47671
47672     /**
47673     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
47674     */
47675     autoExpandMax : 1000,
47676
47677     /**
47678     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
47679     */
47680     view : null,
47681
47682     /**
47683     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
47684     */
47685     loadMask : false,
47686     /**
47687     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
47688     */
47689     dropTarget: false,
47690     
47691    
47692     
47693     // private
47694     rendered : false,
47695
47696     /**
47697     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
47698     * of a fixed width. Default is false.
47699     */
47700     /**
47701     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
47702     */
47703     /**
47704      * Called once after all setup has been completed and the grid is ready to be rendered.
47705      * @return {Roo.grid.Grid} this
47706      */
47707     render : function()
47708     {
47709         var c = this.container;
47710         // try to detect autoHeight/width mode
47711         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
47712             this.autoHeight = true;
47713         }
47714         var view = this.getView();
47715         view.init(this);
47716
47717         c.on("click", this.onClick, this);
47718         c.on("dblclick", this.onDblClick, this);
47719         c.on("contextmenu", this.onContextMenu, this);
47720         c.on("keydown", this.onKeyDown, this);
47721
47722         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
47723
47724         this.getSelectionModel().init(this);
47725
47726         view.render();
47727
47728         if(this.loadMask){
47729             this.loadMask = new Roo.LoadMask(this.container,
47730                     Roo.apply({store:this.dataSource}, this.loadMask));
47731         }
47732         
47733         
47734         if (this.toolbar && this.toolbar.xtype) {
47735             this.toolbar.container = this.getView().getHeaderPanel(true);
47736             this.toolbar = new Roo.Toolbar(this.toolbar);
47737         }
47738         if (this.footer && this.footer.xtype) {
47739             this.footer.dataSource = this.getDataSource();
47740             this.footer.container = this.getView().getFooterPanel(true);
47741             this.footer = Roo.factory(this.footer, Roo);
47742         }
47743         if (this.dropTarget && this.dropTarget.xtype) {
47744             delete this.dropTarget.xtype;
47745             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
47746         }
47747         
47748         
47749         this.rendered = true;
47750         this.fireEvent('render', this);
47751         return this;
47752     },
47753
47754         /**
47755          * Reconfigures the grid to use a different Store and Column Model.
47756          * The View will be bound to the new objects and refreshed.
47757          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
47758          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
47759          */
47760     reconfigure : function(dataSource, colModel){
47761         if(this.loadMask){
47762             this.loadMask.destroy();
47763             this.loadMask = new Roo.LoadMask(this.container,
47764                     Roo.apply({store:dataSource}, this.loadMask));
47765         }
47766         this.view.bind(dataSource, colModel);
47767         this.dataSource = dataSource;
47768         this.colModel = colModel;
47769         this.view.refresh(true);
47770     },
47771
47772     // private
47773     onKeyDown : function(e){
47774         this.fireEvent("keydown", e);
47775     },
47776
47777     /**
47778      * Destroy this grid.
47779      * @param {Boolean} removeEl True to remove the element
47780      */
47781     destroy : function(removeEl, keepListeners){
47782         if(this.loadMask){
47783             this.loadMask.destroy();
47784         }
47785         var c = this.container;
47786         c.removeAllListeners();
47787         this.view.destroy();
47788         this.colModel.purgeListeners();
47789         if(!keepListeners){
47790             this.purgeListeners();
47791         }
47792         c.update("");
47793         if(removeEl === true){
47794             c.remove();
47795         }
47796     },
47797
47798     // private
47799     processEvent : function(name, e){
47800         this.fireEvent(name, e);
47801         var t = e.getTarget();
47802         var v = this.view;
47803         var header = v.findHeaderIndex(t);
47804         if(header !== false){
47805             this.fireEvent("header" + name, this, header, e);
47806         }else{
47807             var row = v.findRowIndex(t);
47808             var cell = v.findCellIndex(t);
47809             if(row !== false){
47810                 this.fireEvent("row" + name, this, row, e);
47811                 if(cell !== false){
47812                     this.fireEvent("cell" + name, this, row, cell, e);
47813                 }
47814             }
47815         }
47816     },
47817
47818     // private
47819     onClick : function(e){
47820         this.processEvent("click", e);
47821     },
47822
47823     // private
47824     onContextMenu : function(e, t){
47825         this.processEvent("contextmenu", e);
47826     },
47827
47828     // private
47829     onDblClick : function(e){
47830         this.processEvent("dblclick", e);
47831     },
47832
47833     // private
47834     walkCells : function(row, col, step, fn, scope){
47835         var cm = this.colModel, clen = cm.getColumnCount();
47836         var ds = this.dataSource, rlen = ds.getCount(), first = true;
47837         if(step < 0){
47838             if(col < 0){
47839                 row--;
47840                 first = false;
47841             }
47842             while(row >= 0){
47843                 if(!first){
47844                     col = clen-1;
47845                 }
47846                 first = false;
47847                 while(col >= 0){
47848                     if(fn.call(scope || this, row, col, cm) === true){
47849                         return [row, col];
47850                     }
47851                     col--;
47852                 }
47853                 row--;
47854             }
47855         } else {
47856             if(col >= clen){
47857                 row++;
47858                 first = false;
47859             }
47860             while(row < rlen){
47861                 if(!first){
47862                     col = 0;
47863                 }
47864                 first = false;
47865                 while(col < clen){
47866                     if(fn.call(scope || this, row, col, cm) === true){
47867                         return [row, col];
47868                     }
47869                     col++;
47870                 }
47871                 row++;
47872             }
47873         }
47874         return null;
47875     },
47876
47877     // private
47878     getSelections : function(){
47879         return this.selModel.getSelections();
47880     },
47881
47882     /**
47883      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
47884      * but if manual update is required this method will initiate it.
47885      */
47886     autoSize : function(){
47887         if(this.rendered){
47888             this.view.layout();
47889             if(this.view.adjustForScroll){
47890                 this.view.adjustForScroll();
47891             }
47892         }
47893     },
47894
47895     /**
47896      * Returns the grid's underlying element.
47897      * @return {Element} The element
47898      */
47899     getGridEl : function(){
47900         return this.container;
47901     },
47902
47903     // private for compatibility, overridden by editor grid
47904     stopEditing : function(){},
47905
47906     /**
47907      * Returns the grid's SelectionModel.
47908      * @return {SelectionModel}
47909      */
47910     getSelectionModel : function(){
47911         if(!this.selModel){
47912             this.selModel = new Roo.grid.RowSelectionModel();
47913         }
47914         return this.selModel;
47915     },
47916
47917     /**
47918      * Returns the grid's DataSource.
47919      * @return {DataSource}
47920      */
47921     getDataSource : function(){
47922         return this.dataSource;
47923     },
47924
47925     /**
47926      * Returns the grid's ColumnModel.
47927      * @return {ColumnModel}
47928      */
47929     getColumnModel : function(){
47930         return this.colModel;
47931     },
47932
47933     /**
47934      * Returns the grid's GridView object.
47935      * @return {GridView}
47936      */
47937     getView : function(){
47938         if(!this.view){
47939             this.view = new Roo.grid.GridView(this.viewConfig);
47940         }
47941         return this.view;
47942     },
47943     /**
47944      * Called to get grid's drag proxy text, by default returns this.ddText.
47945      * @return {String}
47946      */
47947     getDragDropText : function(){
47948         var count = this.selModel.getCount();
47949         return String.format(this.ddText, count, count == 1 ? '' : 's');
47950     }
47951 });
47952 /**
47953  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
47954  * %0 is replaced with the number of selected rows.
47955  * @type String
47956  */
47957 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
47958  * Based on:
47959  * Ext JS Library 1.1.1
47960  * Copyright(c) 2006-2007, Ext JS, LLC.
47961  *
47962  * Originally Released Under LGPL - original licence link has changed is not relivant.
47963  *
47964  * Fork - LGPL
47965  * <script type="text/javascript">
47966  */
47967  
47968 Roo.grid.AbstractGridView = function(){
47969         this.grid = null;
47970         
47971         this.events = {
47972             "beforerowremoved" : true,
47973             "beforerowsinserted" : true,
47974             "beforerefresh" : true,
47975             "rowremoved" : true,
47976             "rowsinserted" : true,
47977             "rowupdated" : true,
47978             "refresh" : true
47979         };
47980     Roo.grid.AbstractGridView.superclass.constructor.call(this);
47981 };
47982
47983 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
47984     rowClass : "x-grid-row",
47985     cellClass : "x-grid-cell",
47986     tdClass : "x-grid-td",
47987     hdClass : "x-grid-hd",
47988     splitClass : "x-grid-hd-split",
47989     
47990         init: function(grid){
47991         this.grid = grid;
47992                 var cid = this.grid.getGridEl().id;
47993         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
47994         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
47995         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
47996         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
47997         },
47998         
47999         getColumnRenderers : function(){
48000         var renderers = [];
48001         var cm = this.grid.colModel;
48002         var colCount = cm.getColumnCount();
48003         for(var i = 0; i < colCount; i++){
48004             renderers[i] = cm.getRenderer(i);
48005         }
48006         return renderers;
48007     },
48008     
48009     getColumnIds : function(){
48010         var ids = [];
48011         var cm = this.grid.colModel;
48012         var colCount = cm.getColumnCount();
48013         for(var i = 0; i < colCount; i++){
48014             ids[i] = cm.getColumnId(i);
48015         }
48016         return ids;
48017     },
48018     
48019     getDataIndexes : function(){
48020         if(!this.indexMap){
48021             this.indexMap = this.buildIndexMap();
48022         }
48023         return this.indexMap.colToData;
48024     },
48025     
48026     getColumnIndexByDataIndex : function(dataIndex){
48027         if(!this.indexMap){
48028             this.indexMap = this.buildIndexMap();
48029         }
48030         return this.indexMap.dataToCol[dataIndex];
48031     },
48032     
48033     /**
48034      * Set a css style for a column dynamically. 
48035      * @param {Number} colIndex The index of the column
48036      * @param {String} name The css property name
48037      * @param {String} value The css value
48038      */
48039     setCSSStyle : function(colIndex, name, value){
48040         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
48041         Roo.util.CSS.updateRule(selector, name, value);
48042     },
48043     
48044     generateRules : function(cm){
48045         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
48046         Roo.util.CSS.removeStyleSheet(rulesId);
48047         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48048             var cid = cm.getColumnId(i);
48049             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
48050                          this.tdSelector, cid, " {\n}\n",
48051                          this.hdSelector, cid, " {\n}\n",
48052                          this.splitSelector, cid, " {\n}\n");
48053         }
48054         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48055     }
48056 });/*
48057  * Based on:
48058  * Ext JS Library 1.1.1
48059  * Copyright(c) 2006-2007, Ext JS, LLC.
48060  *
48061  * Originally Released Under LGPL - original licence link has changed is not relivant.
48062  *
48063  * Fork - LGPL
48064  * <script type="text/javascript">
48065  */
48066
48067 // private
48068 // This is a support class used internally by the Grid components
48069 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
48070     this.grid = grid;
48071     this.view = grid.getView();
48072     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48073     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
48074     if(hd2){
48075         this.setHandleElId(Roo.id(hd));
48076         this.setOuterHandleElId(Roo.id(hd2));
48077     }
48078     this.scroll = false;
48079 };
48080 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
48081     maxDragWidth: 120,
48082     getDragData : function(e){
48083         var t = Roo.lib.Event.getTarget(e);
48084         var h = this.view.findHeaderCell(t);
48085         if(h){
48086             return {ddel: h.firstChild, header:h};
48087         }
48088         return false;
48089     },
48090
48091     onInitDrag : function(e){
48092         this.view.headersDisabled = true;
48093         var clone = this.dragData.ddel.cloneNode(true);
48094         clone.id = Roo.id();
48095         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
48096         this.proxy.update(clone);
48097         return true;
48098     },
48099
48100     afterValidDrop : function(){
48101         var v = this.view;
48102         setTimeout(function(){
48103             v.headersDisabled = false;
48104         }, 50);
48105     },
48106
48107     afterInvalidDrop : function(){
48108         var v = this.view;
48109         setTimeout(function(){
48110             v.headersDisabled = false;
48111         }, 50);
48112     }
48113 });
48114 /*
48115  * Based on:
48116  * Ext JS Library 1.1.1
48117  * Copyright(c) 2006-2007, Ext JS, LLC.
48118  *
48119  * Originally Released Under LGPL - original licence link has changed is not relivant.
48120  *
48121  * Fork - LGPL
48122  * <script type="text/javascript">
48123  */
48124 // private
48125 // This is a support class used internally by the Grid components
48126 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
48127     this.grid = grid;
48128     this.view = grid.getView();
48129     // split the proxies so they don't interfere with mouse events
48130     this.proxyTop = Roo.DomHelper.append(document.body, {
48131         cls:"col-move-top", html:"&#160;"
48132     }, true);
48133     this.proxyBottom = Roo.DomHelper.append(document.body, {
48134         cls:"col-move-bottom", html:"&#160;"
48135     }, true);
48136     this.proxyTop.hide = this.proxyBottom.hide = function(){
48137         this.setLeftTop(-100,-100);
48138         this.setStyle("visibility", "hidden");
48139     };
48140     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
48141     // temporarily disabled
48142     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
48143     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
48144 };
48145 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
48146     proxyOffsets : [-4, -9],
48147     fly: Roo.Element.fly,
48148
48149     getTargetFromEvent : function(e){
48150         var t = Roo.lib.Event.getTarget(e);
48151         var cindex = this.view.findCellIndex(t);
48152         if(cindex !== false){
48153             return this.view.getHeaderCell(cindex);
48154         }
48155         return null;
48156     },
48157
48158     nextVisible : function(h){
48159         var v = this.view, cm = this.grid.colModel;
48160         h = h.nextSibling;
48161         while(h){
48162             if(!cm.isHidden(v.getCellIndex(h))){
48163                 return h;
48164             }
48165             h = h.nextSibling;
48166         }
48167         return null;
48168     },
48169
48170     prevVisible : function(h){
48171         var v = this.view, cm = this.grid.colModel;
48172         h = h.prevSibling;
48173         while(h){
48174             if(!cm.isHidden(v.getCellIndex(h))){
48175                 return h;
48176             }
48177             h = h.prevSibling;
48178         }
48179         return null;
48180     },
48181
48182     positionIndicator : function(h, n, e){
48183         var x = Roo.lib.Event.getPageX(e);
48184         var r = Roo.lib.Dom.getRegion(n.firstChild);
48185         var px, pt, py = r.top + this.proxyOffsets[1];
48186         if((r.right - x) <= (r.right-r.left)/2){
48187             px = r.right+this.view.borderWidth;
48188             pt = "after";
48189         }else{
48190             px = r.left;
48191             pt = "before";
48192         }
48193         var oldIndex = this.view.getCellIndex(h);
48194         var newIndex = this.view.getCellIndex(n);
48195
48196         if(this.grid.colModel.isFixed(newIndex)){
48197             return false;
48198         }
48199
48200         var locked = this.grid.colModel.isLocked(newIndex);
48201
48202         if(pt == "after"){
48203             newIndex++;
48204         }
48205         if(oldIndex < newIndex){
48206             newIndex--;
48207         }
48208         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
48209             return false;
48210         }
48211         px +=  this.proxyOffsets[0];
48212         this.proxyTop.setLeftTop(px, py);
48213         this.proxyTop.show();
48214         if(!this.bottomOffset){
48215             this.bottomOffset = this.view.mainHd.getHeight();
48216         }
48217         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
48218         this.proxyBottom.show();
48219         return pt;
48220     },
48221
48222     onNodeEnter : function(n, dd, e, data){
48223         if(data.header != n){
48224             this.positionIndicator(data.header, n, e);
48225         }
48226     },
48227
48228     onNodeOver : function(n, dd, e, data){
48229         var result = false;
48230         if(data.header != n){
48231             result = this.positionIndicator(data.header, n, e);
48232         }
48233         if(!result){
48234             this.proxyTop.hide();
48235             this.proxyBottom.hide();
48236         }
48237         return result ? this.dropAllowed : this.dropNotAllowed;
48238     },
48239
48240     onNodeOut : function(n, dd, e, data){
48241         this.proxyTop.hide();
48242         this.proxyBottom.hide();
48243     },
48244
48245     onNodeDrop : function(n, dd, e, data){
48246         var h = data.header;
48247         if(h != n){
48248             var cm = this.grid.colModel;
48249             var x = Roo.lib.Event.getPageX(e);
48250             var r = Roo.lib.Dom.getRegion(n.firstChild);
48251             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
48252             var oldIndex = this.view.getCellIndex(h);
48253             var newIndex = this.view.getCellIndex(n);
48254             var locked = cm.isLocked(newIndex);
48255             if(pt == "after"){
48256                 newIndex++;
48257             }
48258             if(oldIndex < newIndex){
48259                 newIndex--;
48260             }
48261             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
48262                 return false;
48263             }
48264             cm.setLocked(oldIndex, locked, true);
48265             cm.moveColumn(oldIndex, newIndex);
48266             this.grid.fireEvent("columnmove", oldIndex, newIndex);
48267             return true;
48268         }
48269         return false;
48270     }
48271 });
48272 /*
48273  * Based on:
48274  * Ext JS Library 1.1.1
48275  * Copyright(c) 2006-2007, Ext JS, LLC.
48276  *
48277  * Originally Released Under LGPL - original licence link has changed is not relivant.
48278  *
48279  * Fork - LGPL
48280  * <script type="text/javascript">
48281  */
48282   
48283 /**
48284  * @class Roo.grid.GridView
48285  * @extends Roo.util.Observable
48286  *
48287  * @constructor
48288  * @param {Object} config
48289  */
48290 Roo.grid.GridView = function(config){
48291     Roo.grid.GridView.superclass.constructor.call(this);
48292     this.el = null;
48293
48294     Roo.apply(this, config);
48295 };
48296
48297 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
48298
48299     /**
48300      * Override this function to apply custom css classes to rows during rendering
48301      * @param {Record} record The record
48302      * @param {Number} index
48303      * @method getRowClass
48304      */
48305     rowClass : "x-grid-row",
48306
48307     cellClass : "x-grid-col",
48308
48309     tdClass : "x-grid-td",
48310
48311     hdClass : "x-grid-hd",
48312
48313     splitClass : "x-grid-split",
48314
48315     sortClasses : ["sort-asc", "sort-desc"],
48316
48317     enableMoveAnim : false,
48318
48319     hlColor: "C3DAF9",
48320
48321     dh : Roo.DomHelper,
48322
48323     fly : Roo.Element.fly,
48324
48325     css : Roo.util.CSS,
48326
48327     borderWidth: 1,
48328
48329     splitOffset: 3,
48330
48331     scrollIncrement : 22,
48332
48333     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
48334
48335     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
48336
48337     bind : function(ds, cm){
48338         if(this.ds){
48339             this.ds.un("load", this.onLoad, this);
48340             this.ds.un("datachanged", this.onDataChange, this);
48341             this.ds.un("add", this.onAdd, this);
48342             this.ds.un("remove", this.onRemove, this);
48343             this.ds.un("update", this.onUpdate, this);
48344             this.ds.un("clear", this.onClear, this);
48345         }
48346         if(ds){
48347             ds.on("load", this.onLoad, this);
48348             ds.on("datachanged", this.onDataChange, this);
48349             ds.on("add", this.onAdd, this);
48350             ds.on("remove", this.onRemove, this);
48351             ds.on("update", this.onUpdate, this);
48352             ds.on("clear", this.onClear, this);
48353         }
48354         this.ds = ds;
48355
48356         if(this.cm){
48357             this.cm.un("widthchange", this.onColWidthChange, this);
48358             this.cm.un("headerchange", this.onHeaderChange, this);
48359             this.cm.un("hiddenchange", this.onHiddenChange, this);
48360             this.cm.un("columnmoved", this.onColumnMove, this);
48361             this.cm.un("columnlockchange", this.onColumnLock, this);
48362         }
48363         if(cm){
48364             this.generateRules(cm);
48365             cm.on("widthchange", this.onColWidthChange, this);
48366             cm.on("headerchange", this.onHeaderChange, this);
48367             cm.on("hiddenchange", this.onHiddenChange, this);
48368             cm.on("columnmoved", this.onColumnMove, this);
48369             cm.on("columnlockchange", this.onColumnLock, this);
48370         }
48371         this.cm = cm;
48372     },
48373
48374     init: function(grid){
48375         Roo.grid.GridView.superclass.init.call(this, grid);
48376
48377         this.bind(grid.dataSource, grid.colModel);
48378
48379         grid.on("headerclick", this.handleHeaderClick, this);
48380
48381         if(grid.trackMouseOver){
48382             grid.on("mouseover", this.onRowOver, this);
48383             grid.on("mouseout", this.onRowOut, this);
48384         }
48385         grid.cancelTextSelection = function(){};
48386         this.gridId = grid.id;
48387
48388         var tpls = this.templates || {};
48389
48390         if(!tpls.master){
48391             tpls.master = new Roo.Template(
48392                '<div class="x-grid" hidefocus="true">',
48393                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
48394                   '<div class="x-grid-topbar"></div>',
48395                   '<div class="x-grid-scroller"><div></div></div>',
48396                   '<div class="x-grid-locked">',
48397                       '<div class="x-grid-header">{lockedHeader}</div>',
48398                       '<div class="x-grid-body">{lockedBody}</div>',
48399                   "</div>",
48400                   '<div class="x-grid-viewport">',
48401                       '<div class="x-grid-header">{header}</div>',
48402                       '<div class="x-grid-body">{body}</div>',
48403                   "</div>",
48404                   '<div class="x-grid-bottombar"></div>',
48405                  
48406                   '<div class="x-grid-resize-proxy">&#160;</div>',
48407                "</div>"
48408             );
48409             tpls.master.disableformats = true;
48410         }
48411
48412         if(!tpls.header){
48413             tpls.header = new Roo.Template(
48414                '<table border="0" cellspacing="0" cellpadding="0">',
48415                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
48416                "</table>{splits}"
48417             );
48418             tpls.header.disableformats = true;
48419         }
48420         tpls.header.compile();
48421
48422         if(!tpls.hcell){
48423             tpls.hcell = new Roo.Template(
48424                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
48425                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
48426                 "</div></td>"
48427              );
48428              tpls.hcell.disableFormats = true;
48429         }
48430         tpls.hcell.compile();
48431
48432         if(!tpls.hsplit){
48433             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
48434             tpls.hsplit.disableFormats = true;
48435         }
48436         tpls.hsplit.compile();
48437
48438         if(!tpls.body){
48439             tpls.body = new Roo.Template(
48440                '<table border="0" cellspacing="0" cellpadding="0">',
48441                "<tbody>{rows}</tbody>",
48442                "</table>"
48443             );
48444             tpls.body.disableFormats = true;
48445         }
48446         tpls.body.compile();
48447
48448         if(!tpls.row){
48449             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
48450             tpls.row.disableFormats = true;
48451         }
48452         tpls.row.compile();
48453
48454         if(!tpls.cell){
48455             tpls.cell = new Roo.Template(
48456                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
48457                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
48458                 "</td>"
48459             );
48460             tpls.cell.disableFormats = true;
48461         }
48462         tpls.cell.compile();
48463
48464         this.templates = tpls;
48465     },
48466
48467     // remap these for backwards compat
48468     onColWidthChange : function(){
48469         this.updateColumns.apply(this, arguments);
48470     },
48471     onHeaderChange : function(){
48472         this.updateHeaders.apply(this, arguments);
48473     }, 
48474     onHiddenChange : function(){
48475         this.handleHiddenChange.apply(this, arguments);
48476     },
48477     onColumnMove : function(){
48478         this.handleColumnMove.apply(this, arguments);
48479     },
48480     onColumnLock : function(){
48481         this.handleLockChange.apply(this, arguments);
48482     },
48483
48484     onDataChange : function(){
48485         this.refresh();
48486         this.updateHeaderSortState();
48487     },
48488
48489     onClear : function(){
48490         this.refresh();
48491     },
48492
48493     onUpdate : function(ds, record){
48494         this.refreshRow(record);
48495     },
48496
48497     refreshRow : function(record){
48498         var ds = this.ds, index;
48499         if(typeof record == 'number'){
48500             index = record;
48501             record = ds.getAt(index);
48502         }else{
48503             index = ds.indexOf(record);
48504         }
48505         this.insertRows(ds, index, index, true);
48506         this.onRemove(ds, record, index+1, true);
48507         this.syncRowHeights(index, index);
48508         this.layout();
48509         this.fireEvent("rowupdated", this, index, record);
48510     },
48511
48512     onAdd : function(ds, records, index){
48513         this.insertRows(ds, index, index + (records.length-1));
48514     },
48515
48516     onRemove : function(ds, record, index, isUpdate){
48517         if(isUpdate !== true){
48518             this.fireEvent("beforerowremoved", this, index, record);
48519         }
48520         var bt = this.getBodyTable(), lt = this.getLockedTable();
48521         if(bt.rows[index]){
48522             bt.firstChild.removeChild(bt.rows[index]);
48523         }
48524         if(lt.rows[index]){
48525             lt.firstChild.removeChild(lt.rows[index]);
48526         }
48527         if(isUpdate !== true){
48528             this.stripeRows(index);
48529             this.syncRowHeights(index, index);
48530             this.layout();
48531             this.fireEvent("rowremoved", this, index, record);
48532         }
48533     },
48534
48535     onLoad : function(){
48536         this.scrollToTop();
48537     },
48538
48539     /**
48540      * Scrolls the grid to the top
48541      */
48542     scrollToTop : function(){
48543         if(this.scroller){
48544             this.scroller.dom.scrollTop = 0;
48545             this.syncScroll();
48546         }
48547     },
48548
48549     /**
48550      * Gets a panel in the header of the grid that can be used for toolbars etc.
48551      * After modifying the contents of this panel a call to grid.autoSize() may be
48552      * required to register any changes in size.
48553      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
48554      * @return Roo.Element
48555      */
48556     getHeaderPanel : function(doShow){
48557         if(doShow){
48558             this.headerPanel.show();
48559         }
48560         return this.headerPanel;
48561     },
48562
48563     /**
48564      * Gets a panel in the footer of the grid that can be used for toolbars etc.
48565      * After modifying the contents of this panel a call to grid.autoSize() may be
48566      * required to register any changes in size.
48567      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
48568      * @return Roo.Element
48569      */
48570     getFooterPanel : function(doShow){
48571         if(doShow){
48572             this.footerPanel.show();
48573         }
48574         return this.footerPanel;
48575     },
48576
48577     initElements : function(){
48578         var E = Roo.Element;
48579         var el = this.grid.getGridEl().dom.firstChild;
48580         var cs = el.childNodes;
48581
48582         this.el = new E(el);
48583         
48584          this.focusEl = new E(el.firstChild);
48585         this.focusEl.swallowEvent("click", true);
48586         
48587         this.headerPanel = new E(cs[1]);
48588         this.headerPanel.enableDisplayMode("block");
48589
48590         this.scroller = new E(cs[2]);
48591         this.scrollSizer = new E(this.scroller.dom.firstChild);
48592
48593         this.lockedWrap = new E(cs[3]);
48594         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
48595         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
48596
48597         this.mainWrap = new E(cs[4]);
48598         this.mainHd = new E(this.mainWrap.dom.firstChild);
48599         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
48600
48601         this.footerPanel = new E(cs[5]);
48602         this.footerPanel.enableDisplayMode("block");
48603
48604         this.resizeProxy = new E(cs[6]);
48605
48606         this.headerSelector = String.format(
48607            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
48608            this.lockedHd.id, this.mainHd.id
48609         );
48610
48611         this.splitterSelector = String.format(
48612            '#{0} div.x-grid-split, #{1} div.x-grid-split',
48613            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
48614         );
48615     },
48616     idToCssName : function(s)
48617     {
48618         return s.replace(/[^a-z0-9]+/ig, '-');
48619     },
48620
48621     getHeaderCell : function(index){
48622         return Roo.DomQuery.select(this.headerSelector)[index];
48623     },
48624
48625     getHeaderCellMeasure : function(index){
48626         return this.getHeaderCell(index).firstChild;
48627     },
48628
48629     getHeaderCellText : function(index){
48630         return this.getHeaderCell(index).firstChild.firstChild;
48631     },
48632
48633     getLockedTable : function(){
48634         return this.lockedBody.dom.firstChild;
48635     },
48636
48637     getBodyTable : function(){
48638         return this.mainBody.dom.firstChild;
48639     },
48640
48641     getLockedRow : function(index){
48642         return this.getLockedTable().rows[index];
48643     },
48644
48645     getRow : function(index){
48646         return this.getBodyTable().rows[index];
48647     },
48648
48649     getRowComposite : function(index){
48650         if(!this.rowEl){
48651             this.rowEl = new Roo.CompositeElementLite();
48652         }
48653         var els = [], lrow, mrow;
48654         if(lrow = this.getLockedRow(index)){
48655             els.push(lrow);
48656         }
48657         if(mrow = this.getRow(index)){
48658             els.push(mrow);
48659         }
48660         this.rowEl.elements = els;
48661         return this.rowEl;
48662     },
48663     /**
48664      * Gets the 'td' of the cell
48665      * 
48666      * @param {Integer} rowIndex row to select
48667      * @param {Integer} colIndex column to select
48668      * 
48669      * @return {Object} 
48670      */
48671     getCell : function(rowIndex, colIndex){
48672         var locked = this.cm.getLockedCount();
48673         var source;
48674         if(colIndex < locked){
48675             source = this.lockedBody.dom.firstChild;
48676         }else{
48677             source = this.mainBody.dom.firstChild;
48678             colIndex -= locked;
48679         }
48680         return source.rows[rowIndex].childNodes[colIndex];
48681     },
48682
48683     getCellText : function(rowIndex, colIndex){
48684         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
48685     },
48686
48687     getCellBox : function(cell){
48688         var b = this.fly(cell).getBox();
48689         if(Roo.isOpera){ // opera fails to report the Y
48690             b.y = cell.offsetTop + this.mainBody.getY();
48691         }
48692         return b;
48693     },
48694
48695     getCellIndex : function(cell){
48696         var id = String(cell.className).match(this.cellRE);
48697         if(id){
48698             return parseInt(id[1], 10);
48699         }
48700         return 0;
48701     },
48702
48703     findHeaderIndex : function(n){
48704         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48705         return r ? this.getCellIndex(r) : false;
48706     },
48707
48708     findHeaderCell : function(n){
48709         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
48710         return r ? r : false;
48711     },
48712
48713     findRowIndex : function(n){
48714         if(!n){
48715             return false;
48716         }
48717         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
48718         return r ? r.rowIndex : false;
48719     },
48720
48721     findCellIndex : function(node){
48722         var stop = this.el.dom;
48723         while(node && node != stop){
48724             if(this.findRE.test(node.className)){
48725                 return this.getCellIndex(node);
48726             }
48727             node = node.parentNode;
48728         }
48729         return false;
48730     },
48731
48732     getColumnId : function(index){
48733         return this.cm.getColumnId(index);
48734     },
48735
48736     getSplitters : function()
48737     {
48738         if(this.splitterSelector){
48739            return Roo.DomQuery.select(this.splitterSelector);
48740         }else{
48741             return null;
48742       }
48743     },
48744
48745     getSplitter : function(index){
48746         return this.getSplitters()[index];
48747     },
48748
48749     onRowOver : function(e, t){
48750         var row;
48751         if((row = this.findRowIndex(t)) !== false){
48752             this.getRowComposite(row).addClass("x-grid-row-over");
48753         }
48754     },
48755
48756     onRowOut : function(e, t){
48757         var row;
48758         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
48759             this.getRowComposite(row).removeClass("x-grid-row-over");
48760         }
48761     },
48762
48763     renderHeaders : function(){
48764         var cm = this.cm;
48765         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
48766         var cb = [], lb = [], sb = [], lsb = [], p = {};
48767         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48768             p.cellId = "x-grid-hd-0-" + i;
48769             p.splitId = "x-grid-csplit-0-" + i;
48770             p.id = cm.getColumnId(i);
48771             p.title = cm.getColumnTooltip(i) || "";
48772             p.value = cm.getColumnHeader(i) || "";
48773             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
48774             if(!cm.isLocked(i)){
48775                 cb[cb.length] = ct.apply(p);
48776                 sb[sb.length] = st.apply(p);
48777             }else{
48778                 lb[lb.length] = ct.apply(p);
48779                 lsb[lsb.length] = st.apply(p);
48780             }
48781         }
48782         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
48783                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
48784     },
48785
48786     updateHeaders : function(){
48787         var html = this.renderHeaders();
48788         this.lockedHd.update(html[0]);
48789         this.mainHd.update(html[1]);
48790     },
48791
48792     /**
48793      * Focuses the specified row.
48794      * @param {Number} row The row index
48795      */
48796     focusRow : function(row)
48797     {
48798         //Roo.log('GridView.focusRow');
48799         var x = this.scroller.dom.scrollLeft;
48800         this.focusCell(row, 0, false);
48801         this.scroller.dom.scrollLeft = x;
48802     },
48803
48804     /**
48805      * Focuses the specified cell.
48806      * @param {Number} row The row index
48807      * @param {Number} col The column index
48808      * @param {Boolean} hscroll false to disable horizontal scrolling
48809      */
48810     focusCell : function(row, col, hscroll)
48811     {
48812         //Roo.log('GridView.focusCell');
48813         var el = this.ensureVisible(row, col, hscroll);
48814         this.focusEl.alignTo(el, "tl-tl");
48815         if(Roo.isGecko){
48816             this.focusEl.focus();
48817         }else{
48818             this.focusEl.focus.defer(1, this.focusEl);
48819         }
48820     },
48821
48822     /**
48823      * Scrolls the specified cell into view
48824      * @param {Number} row The row index
48825      * @param {Number} col The column index
48826      * @param {Boolean} hscroll false to disable horizontal scrolling
48827      */
48828     ensureVisible : function(row, col, hscroll)
48829     {
48830         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
48831         //return null; //disable for testing.
48832         if(typeof row != "number"){
48833             row = row.rowIndex;
48834         }
48835         if(row < 0 && row >= this.ds.getCount()){
48836             return  null;
48837         }
48838         col = (col !== undefined ? col : 0);
48839         var cm = this.grid.colModel;
48840         while(cm.isHidden(col)){
48841             col++;
48842         }
48843
48844         var el = this.getCell(row, col);
48845         if(!el){
48846             return null;
48847         }
48848         var c = this.scroller.dom;
48849
48850         var ctop = parseInt(el.offsetTop, 10);
48851         var cleft = parseInt(el.offsetLeft, 10);
48852         var cbot = ctop + el.offsetHeight;
48853         var cright = cleft + el.offsetWidth;
48854         
48855         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
48856         var stop = parseInt(c.scrollTop, 10);
48857         var sleft = parseInt(c.scrollLeft, 10);
48858         var sbot = stop + ch;
48859         var sright = sleft + c.clientWidth;
48860         /*
48861         Roo.log('GridView.ensureVisible:' +
48862                 ' ctop:' + ctop +
48863                 ' c.clientHeight:' + c.clientHeight +
48864                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
48865                 ' stop:' + stop +
48866                 ' cbot:' + cbot +
48867                 ' sbot:' + sbot +
48868                 ' ch:' + ch  
48869                 );
48870         */
48871         if(ctop < stop){
48872              c.scrollTop = ctop;
48873             //Roo.log("set scrolltop to ctop DISABLE?");
48874         }else if(cbot > sbot){
48875             //Roo.log("set scrolltop to cbot-ch");
48876             c.scrollTop = cbot-ch;
48877         }
48878         
48879         if(hscroll !== false){
48880             if(cleft < sleft){
48881                 c.scrollLeft = cleft;
48882             }else if(cright > sright){
48883                 c.scrollLeft = cright-c.clientWidth;
48884             }
48885         }
48886          
48887         return el;
48888     },
48889
48890     updateColumns : function(){
48891         this.grid.stopEditing();
48892         var cm = this.grid.colModel, colIds = this.getColumnIds();
48893         //var totalWidth = cm.getTotalWidth();
48894         var pos = 0;
48895         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48896             //if(cm.isHidden(i)) continue;
48897             var w = cm.getColumnWidth(i);
48898             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48899             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
48900         }
48901         this.updateSplitters();
48902     },
48903
48904     generateRules : function(cm){
48905         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
48906         Roo.util.CSS.removeStyleSheet(rulesId);
48907         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48908             var cid = cm.getColumnId(i);
48909             var align = '';
48910             if(cm.config[i].align){
48911                 align = 'text-align:'+cm.config[i].align+';';
48912             }
48913             var hidden = '';
48914             if(cm.isHidden(i)){
48915                 hidden = 'display:none;';
48916             }
48917             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
48918             ruleBuf.push(
48919                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
48920                     this.hdSelector, cid, " {\n", align, width, "}\n",
48921                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
48922                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
48923         }
48924         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
48925     },
48926
48927     updateSplitters : function(){
48928         var cm = this.cm, s = this.getSplitters();
48929         if(s){ // splitters not created yet
48930             var pos = 0, locked = true;
48931             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
48932                 if(cm.isHidden(i)) continue;
48933                 var w = cm.getColumnWidth(i); // make sure it's a number
48934                 if(!cm.isLocked(i) && locked){
48935                     pos = 0;
48936                     locked = false;
48937                 }
48938                 pos += w;
48939                 s[i].style.left = (pos-this.splitOffset) + "px";
48940             }
48941         }
48942     },
48943
48944     handleHiddenChange : function(colModel, colIndex, hidden){
48945         if(hidden){
48946             this.hideColumn(colIndex);
48947         }else{
48948             this.unhideColumn(colIndex);
48949         }
48950     },
48951
48952     hideColumn : function(colIndex){
48953         var cid = this.getColumnId(colIndex);
48954         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
48955         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
48956         if(Roo.isSafari){
48957             this.updateHeaders();
48958         }
48959         this.updateSplitters();
48960         this.layout();
48961     },
48962
48963     unhideColumn : function(colIndex){
48964         var cid = this.getColumnId(colIndex);
48965         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
48966         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
48967
48968         if(Roo.isSafari){
48969             this.updateHeaders();
48970         }
48971         this.updateSplitters();
48972         this.layout();
48973     },
48974
48975     insertRows : function(dm, firstRow, lastRow, isUpdate){
48976         if(firstRow == 0 && lastRow == dm.getCount()-1){
48977             this.refresh();
48978         }else{
48979             if(!isUpdate){
48980                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
48981             }
48982             var s = this.getScrollState();
48983             var markup = this.renderRows(firstRow, lastRow);
48984             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
48985             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
48986             this.restoreScroll(s);
48987             if(!isUpdate){
48988                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
48989                 this.syncRowHeights(firstRow, lastRow);
48990                 this.stripeRows(firstRow);
48991                 this.layout();
48992             }
48993         }
48994     },
48995
48996     bufferRows : function(markup, target, index){
48997         var before = null, trows = target.rows, tbody = target.tBodies[0];
48998         if(index < trows.length){
48999             before = trows[index];
49000         }
49001         var b = document.createElement("div");
49002         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
49003         var rows = b.firstChild.rows;
49004         for(var i = 0, len = rows.length; i < len; i++){
49005             if(before){
49006                 tbody.insertBefore(rows[0], before);
49007             }else{
49008                 tbody.appendChild(rows[0]);
49009             }
49010         }
49011         b.innerHTML = "";
49012         b = null;
49013     },
49014
49015     deleteRows : function(dm, firstRow, lastRow){
49016         if(dm.getRowCount()<1){
49017             this.fireEvent("beforerefresh", this);
49018             this.mainBody.update("");
49019             this.lockedBody.update("");
49020             this.fireEvent("refresh", this);
49021         }else{
49022             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
49023             var bt = this.getBodyTable();
49024             var tbody = bt.firstChild;
49025             var rows = bt.rows;
49026             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
49027                 tbody.removeChild(rows[firstRow]);
49028             }
49029             this.stripeRows(firstRow);
49030             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
49031         }
49032     },
49033
49034     updateRows : function(dataSource, firstRow, lastRow){
49035         var s = this.getScrollState();
49036         this.refresh();
49037         this.restoreScroll(s);
49038     },
49039
49040     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
49041         if(!noRefresh){
49042            this.refresh();
49043         }
49044         this.updateHeaderSortState();
49045     },
49046
49047     getScrollState : function(){
49048         
49049         var sb = this.scroller.dom;
49050         return {left: sb.scrollLeft, top: sb.scrollTop};
49051     },
49052
49053     stripeRows : function(startRow){
49054         if(!this.grid.stripeRows || this.ds.getCount() < 1){
49055             return;
49056         }
49057         startRow = startRow || 0;
49058         var rows = this.getBodyTable().rows;
49059         var lrows = this.getLockedTable().rows;
49060         var cls = ' x-grid-row-alt ';
49061         for(var i = startRow, len = rows.length; i < len; i++){
49062             var row = rows[i], lrow = lrows[i];
49063             var isAlt = ((i+1) % 2 == 0);
49064             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
49065             if(isAlt == hasAlt){
49066                 continue;
49067             }
49068             if(isAlt){
49069                 row.className += " x-grid-row-alt";
49070             }else{
49071                 row.className = row.className.replace("x-grid-row-alt", "");
49072             }
49073             if(lrow){
49074                 lrow.className = row.className;
49075             }
49076         }
49077     },
49078
49079     restoreScroll : function(state){
49080         //Roo.log('GridView.restoreScroll');
49081         var sb = this.scroller.dom;
49082         sb.scrollLeft = state.left;
49083         sb.scrollTop = state.top;
49084         this.syncScroll();
49085     },
49086
49087     syncScroll : function(){
49088         //Roo.log('GridView.syncScroll');
49089         var sb = this.scroller.dom;
49090         var sh = this.mainHd.dom;
49091         var bs = this.mainBody.dom;
49092         var lv = this.lockedBody.dom;
49093         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
49094         lv.scrollTop = bs.scrollTop = sb.scrollTop;
49095     },
49096
49097     handleScroll : function(e){
49098         this.syncScroll();
49099         var sb = this.scroller.dom;
49100         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
49101         e.stopEvent();
49102     },
49103
49104     handleWheel : function(e){
49105         var d = e.getWheelDelta();
49106         this.scroller.dom.scrollTop -= d*22;
49107         // set this here to prevent jumpy scrolling on large tables
49108         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
49109         e.stopEvent();
49110     },
49111
49112     renderRows : function(startRow, endRow){
49113         // pull in all the crap needed to render rows
49114         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
49115         var colCount = cm.getColumnCount();
49116
49117         if(ds.getCount() < 1){
49118             return ["", ""];
49119         }
49120
49121         // build a map for all the columns
49122         var cs = [];
49123         for(var i = 0; i < colCount; i++){
49124             var name = cm.getDataIndex(i);
49125             cs[i] = {
49126                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
49127                 renderer : cm.getRenderer(i),
49128                 id : cm.getColumnId(i),
49129                 locked : cm.isLocked(i)
49130             };
49131         }
49132
49133         startRow = startRow || 0;
49134         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
49135
49136         // records to render
49137         var rs = ds.getRange(startRow, endRow);
49138
49139         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
49140     },
49141
49142     // As much as I hate to duplicate code, this was branched because FireFox really hates
49143     // [].join("") on strings. The performance difference was substantial enough to
49144     // branch this function
49145     doRender : Roo.isGecko ?
49146             function(cs, rs, ds, startRow, colCount, stripe){
49147                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49148                 // buffers
49149                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49150                 
49151                 var hasListener = this.grid.hasListener('rowclass');
49152                 var rowcfg = {};
49153                 for(var j = 0, len = rs.length; j < len; j++){
49154                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
49155                     for(var i = 0; i < colCount; i++){
49156                         c = cs[i];
49157                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49158                         p.id = c.id;
49159                         p.css = p.attr = "";
49160                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49161                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49162                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49163                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49164                         }
49165                         var markup = ct.apply(p);
49166                         if(!c.locked){
49167                             cb+= markup;
49168                         }else{
49169                             lcb+= markup;
49170                         }
49171                     }
49172                     var alt = [];
49173                     if(stripe && ((rowIndex+1) % 2 == 0)){
49174                         alt.push("x-grid-row-alt")
49175                     }
49176                     if(r.dirty){
49177                         alt.push(  " x-grid-dirty-row");
49178                     }
49179                     rp.cells = lcb;
49180                     if(this.getRowClass){
49181                         alt.push(this.getRowClass(r, rowIndex));
49182                     }
49183                     if (hasListener) {
49184                         rowcfg = {
49185                              
49186                             record: r,
49187                             rowIndex : rowIndex,
49188                             rowClass : ''
49189                         }
49190                         this.grid.fireEvent('rowclass', this, rowcfg);
49191                         alt.push(rowcfg.rowClass);
49192                     }
49193                     rp.alt = alt.join(" ");
49194                     lbuf+= rt.apply(rp);
49195                     rp.cells = cb;
49196                     buf+=  rt.apply(rp);
49197                 }
49198                 return [lbuf, buf];
49199             } :
49200             function(cs, rs, ds, startRow, colCount, stripe){
49201                 var ts = this.templates, ct = ts.cell, rt = ts.row;
49202                 // buffers
49203                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
49204                 var hasListener = this.grid.hasListener('rowclass');
49205                 var rowcfg = {};
49206                 for(var j = 0, len = rs.length; j < len; j++){
49207                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
49208                     for(var i = 0; i < colCount; i++){
49209                         c = cs[i];
49210                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
49211                         p.id = c.id;
49212                         p.css = p.attr = "";
49213                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
49214                         if(p.value == undefined || p.value === "") p.value = "&#160;";
49215                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
49216                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
49217                         }
49218                         var markup = ct.apply(p);
49219                         if(!c.locked){
49220                             cb[cb.length] = markup;
49221                         }else{
49222                             lcb[lcb.length] = markup;
49223                         }
49224                     }
49225                     var alt = [];
49226                     if(stripe && ((rowIndex+1) % 2 == 0)){
49227                         alt.push( "x-grid-row-alt");
49228                     }
49229                     if(r.dirty){
49230                         alt.push(" x-grid-dirty-row");
49231                     }
49232                     rp.cells = lcb;
49233                     if(this.getRowClass){
49234                         alt.push( this.getRowClass(r, rowIndex));
49235                     }
49236                     if (hasListener) {
49237                         rowcfg = {
49238                              
49239                             record: r,
49240                             rowIndex : rowIndex,
49241                             rowClass : ''
49242                         }
49243                         this.grid.fireEvent('rowclass', this, rowcfg);
49244                         alt.push(rowcfg.rowClass);
49245                     }
49246                     rp.alt = alt.join(" ");
49247                     rp.cells = lcb.join("");
49248                     lbuf[lbuf.length] = rt.apply(rp);
49249                     rp.cells = cb.join("");
49250                     buf[buf.length] =  rt.apply(rp);
49251                 }
49252                 return [lbuf.join(""), buf.join("")];
49253             },
49254
49255     renderBody : function(){
49256         var markup = this.renderRows();
49257         var bt = this.templates.body;
49258         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
49259     },
49260
49261     /**
49262      * Refreshes the grid
49263      * @param {Boolean} headersToo
49264      */
49265     refresh : function(headersToo){
49266         this.fireEvent("beforerefresh", this);
49267         this.grid.stopEditing();
49268         var result = this.renderBody();
49269         this.lockedBody.update(result[0]);
49270         this.mainBody.update(result[1]);
49271         if(headersToo === true){
49272             this.updateHeaders();
49273             this.updateColumns();
49274             this.updateSplitters();
49275             this.updateHeaderSortState();
49276         }
49277         this.syncRowHeights();
49278         this.layout();
49279         this.fireEvent("refresh", this);
49280     },
49281
49282     handleColumnMove : function(cm, oldIndex, newIndex){
49283         this.indexMap = null;
49284         var s = this.getScrollState();
49285         this.refresh(true);
49286         this.restoreScroll(s);
49287         this.afterMove(newIndex);
49288     },
49289
49290     afterMove : function(colIndex){
49291         if(this.enableMoveAnim && Roo.enableFx){
49292             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
49293         }
49294         // if multisort - fix sortOrder, and reload..
49295         if (this.grid.dataSource.multiSort) {
49296             // the we can call sort again..
49297             var dm = this.grid.dataSource;
49298             var cm = this.grid.colModel;
49299             var so = [];
49300             for(var i = 0; i < cm.config.length; i++ ) {
49301                 
49302                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
49303                     continue; // dont' bother, it's not in sort list or being set.
49304                 }
49305                 
49306                 so.push(cm.config[i].dataIndex);
49307             };
49308             dm.sortOrder = so;
49309             dm.load(dm.lastOptions);
49310             
49311             
49312         }
49313         
49314     },
49315
49316     updateCell : function(dm, rowIndex, dataIndex){
49317         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
49318         if(typeof colIndex == "undefined"){ // not present in grid
49319             return;
49320         }
49321         var cm = this.grid.colModel;
49322         var cell = this.getCell(rowIndex, colIndex);
49323         var cellText = this.getCellText(rowIndex, colIndex);
49324
49325         var p = {
49326             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
49327             id : cm.getColumnId(colIndex),
49328             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
49329         };
49330         var renderer = cm.getRenderer(colIndex);
49331         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
49332         if(typeof val == "undefined" || val === "") val = "&#160;";
49333         cellText.innerHTML = val;
49334         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
49335         this.syncRowHeights(rowIndex, rowIndex);
49336     },
49337
49338     calcColumnWidth : function(colIndex, maxRowsToMeasure){
49339         var maxWidth = 0;
49340         if(this.grid.autoSizeHeaders){
49341             var h = this.getHeaderCellMeasure(colIndex);
49342             maxWidth = Math.max(maxWidth, h.scrollWidth);
49343         }
49344         var tb, index;
49345         if(this.cm.isLocked(colIndex)){
49346             tb = this.getLockedTable();
49347             index = colIndex;
49348         }else{
49349             tb = this.getBodyTable();
49350             index = colIndex - this.cm.getLockedCount();
49351         }
49352         if(tb && tb.rows){
49353             var rows = tb.rows;
49354             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
49355             for(var i = 0; i < stopIndex; i++){
49356                 var cell = rows[i].childNodes[index].firstChild;
49357                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
49358             }
49359         }
49360         return maxWidth + /*margin for error in IE*/ 5;
49361     },
49362     /**
49363      * Autofit a column to its content.
49364      * @param {Number} colIndex
49365      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
49366      */
49367      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
49368          if(this.cm.isHidden(colIndex)){
49369              return; // can't calc a hidden column
49370          }
49371         if(forceMinSize){
49372             var cid = this.cm.getColumnId(colIndex);
49373             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
49374            if(this.grid.autoSizeHeaders){
49375                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
49376            }
49377         }
49378         var newWidth = this.calcColumnWidth(colIndex);
49379         this.cm.setColumnWidth(colIndex,
49380             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
49381         if(!suppressEvent){
49382             this.grid.fireEvent("columnresize", colIndex, newWidth);
49383         }
49384     },
49385
49386     /**
49387      * Autofits all columns to their content and then expands to fit any extra space in the grid
49388      */
49389      autoSizeColumns : function(){
49390         var cm = this.grid.colModel;
49391         var colCount = cm.getColumnCount();
49392         for(var i = 0; i < colCount; i++){
49393             this.autoSizeColumn(i, true, true);
49394         }
49395         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
49396             this.fitColumns();
49397         }else{
49398             this.updateColumns();
49399             this.layout();
49400         }
49401     },
49402
49403     /**
49404      * Autofits all columns to the grid's width proportionate with their current size
49405      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
49406      */
49407     fitColumns : function(reserveScrollSpace){
49408         var cm = this.grid.colModel;
49409         var colCount = cm.getColumnCount();
49410         var cols = [];
49411         var width = 0;
49412         var i, w;
49413         for (i = 0; i < colCount; i++){
49414             if(!cm.isHidden(i) && !cm.isFixed(i)){
49415                 w = cm.getColumnWidth(i);
49416                 cols.push(i);
49417                 cols.push(w);
49418                 width += w;
49419             }
49420         }
49421         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
49422         if(reserveScrollSpace){
49423             avail -= 17;
49424         }
49425         var frac = (avail - cm.getTotalWidth())/width;
49426         while (cols.length){
49427             w = cols.pop();
49428             i = cols.pop();
49429             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
49430         }
49431         this.updateColumns();
49432         this.layout();
49433     },
49434
49435     onRowSelect : function(rowIndex){
49436         var row = this.getRowComposite(rowIndex);
49437         row.addClass("x-grid-row-selected");
49438     },
49439
49440     onRowDeselect : function(rowIndex){
49441         var row = this.getRowComposite(rowIndex);
49442         row.removeClass("x-grid-row-selected");
49443     },
49444
49445     onCellSelect : function(row, col){
49446         var cell = this.getCell(row, col);
49447         if(cell){
49448             Roo.fly(cell).addClass("x-grid-cell-selected");
49449         }
49450     },
49451
49452     onCellDeselect : function(row, col){
49453         var cell = this.getCell(row, col);
49454         if(cell){
49455             Roo.fly(cell).removeClass("x-grid-cell-selected");
49456         }
49457     },
49458
49459     updateHeaderSortState : function(){
49460         
49461         // sort state can be single { field: xxx, direction : yyy}
49462         // or   { xxx=>ASC , yyy : DESC ..... }
49463         
49464         var mstate = {};
49465         if (!this.ds.multiSort) { 
49466             var state = this.ds.getSortState();
49467             if(!state){
49468                 return;
49469             }
49470             mstate[state.field] = state.direction;
49471             // FIXME... - this is not used here.. but might be elsewhere..
49472             this.sortState = state;
49473             
49474         } else {
49475             mstate = this.ds.sortToggle;
49476         }
49477         //remove existing sort classes..
49478         
49479         var sc = this.sortClasses;
49480         var hds = this.el.select(this.headerSelector).removeClass(sc);
49481         
49482         for(var f in mstate) {
49483         
49484             var sortColumn = this.cm.findColumnIndex(f);
49485             
49486             if(sortColumn != -1){
49487                 var sortDir = mstate[f];        
49488                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
49489             }
49490         }
49491         
49492          
49493         
49494     },
49495
49496
49497     handleHeaderClick : function(g, index){
49498         if(this.headersDisabled){
49499             return;
49500         }
49501         var dm = g.dataSource, cm = g.colModel;
49502         if(!cm.isSortable(index)){
49503             return;
49504         }
49505         g.stopEditing();
49506         
49507         if (dm.multiSort) {
49508             // update the sortOrder
49509             var so = [];
49510             for(var i = 0; i < cm.config.length; i++ ) {
49511                 
49512                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
49513                     continue; // dont' bother, it's not in sort list or being set.
49514                 }
49515                 
49516                 so.push(cm.config[i].dataIndex);
49517             };
49518             dm.sortOrder = so;
49519         }
49520         
49521         
49522         dm.sort(cm.getDataIndex(index));
49523     },
49524
49525
49526     destroy : function(){
49527         if(this.colMenu){
49528             this.colMenu.removeAll();
49529             Roo.menu.MenuMgr.unregister(this.colMenu);
49530             this.colMenu.getEl().remove();
49531             delete this.colMenu;
49532         }
49533         if(this.hmenu){
49534             this.hmenu.removeAll();
49535             Roo.menu.MenuMgr.unregister(this.hmenu);
49536             this.hmenu.getEl().remove();
49537             delete this.hmenu;
49538         }
49539         if(this.grid.enableColumnMove){
49540             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49541             if(dds){
49542                 for(var dd in dds){
49543                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
49544                         var elid = dds[dd].dragElId;
49545                         dds[dd].unreg();
49546                         Roo.get(elid).remove();
49547                     } else if(dds[dd].config.isTarget){
49548                         dds[dd].proxyTop.remove();
49549                         dds[dd].proxyBottom.remove();
49550                         dds[dd].unreg();
49551                     }
49552                     if(Roo.dd.DDM.locationCache[dd]){
49553                         delete Roo.dd.DDM.locationCache[dd];
49554                     }
49555                 }
49556                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
49557             }
49558         }
49559         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
49560         this.bind(null, null);
49561         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
49562     },
49563
49564     handleLockChange : function(){
49565         this.refresh(true);
49566     },
49567
49568     onDenyColumnLock : function(){
49569
49570     },
49571
49572     onDenyColumnHide : function(){
49573
49574     },
49575
49576     handleHdMenuClick : function(item){
49577         var index = this.hdCtxIndex;
49578         var cm = this.cm, ds = this.ds;
49579         switch(item.id){
49580             case "asc":
49581                 ds.sort(cm.getDataIndex(index), "ASC");
49582                 break;
49583             case "desc":
49584                 ds.sort(cm.getDataIndex(index), "DESC");
49585                 break;
49586             case "lock":
49587                 var lc = cm.getLockedCount();
49588                 if(cm.getColumnCount(true) <= lc+1){
49589                     this.onDenyColumnLock();
49590                     return;
49591                 }
49592                 if(lc != index){
49593                     cm.setLocked(index, true, true);
49594                     cm.moveColumn(index, lc);
49595                     this.grid.fireEvent("columnmove", index, lc);
49596                 }else{
49597                     cm.setLocked(index, true);
49598                 }
49599             break;
49600             case "unlock":
49601                 var lc = cm.getLockedCount();
49602                 if((lc-1) != index){
49603                     cm.setLocked(index, false, true);
49604                     cm.moveColumn(index, lc-1);
49605                     this.grid.fireEvent("columnmove", index, lc-1);
49606                 }else{
49607                     cm.setLocked(index, false);
49608                 }
49609             break;
49610             default:
49611                 index = cm.getIndexById(item.id.substr(4));
49612                 if(index != -1){
49613                     if(item.checked && cm.getColumnCount(true) <= 1){
49614                         this.onDenyColumnHide();
49615                         return false;
49616                     }
49617                     cm.setHidden(index, item.checked);
49618                 }
49619         }
49620         return true;
49621     },
49622
49623     beforeColMenuShow : function(){
49624         var cm = this.cm,  colCount = cm.getColumnCount();
49625         this.colMenu.removeAll();
49626         for(var i = 0; i < colCount; i++){
49627             this.colMenu.add(new Roo.menu.CheckItem({
49628                 id: "col-"+cm.getColumnId(i),
49629                 text: cm.getColumnHeader(i),
49630                 checked: !cm.isHidden(i),
49631                 hideOnClick:false
49632             }));
49633         }
49634     },
49635
49636     handleHdCtx : function(g, index, e){
49637         e.stopEvent();
49638         var hd = this.getHeaderCell(index);
49639         this.hdCtxIndex = index;
49640         var ms = this.hmenu.items, cm = this.cm;
49641         ms.get("asc").setDisabled(!cm.isSortable(index));
49642         ms.get("desc").setDisabled(!cm.isSortable(index));
49643         if(this.grid.enableColLock !== false){
49644             ms.get("lock").setDisabled(cm.isLocked(index));
49645             ms.get("unlock").setDisabled(!cm.isLocked(index));
49646         }
49647         this.hmenu.show(hd, "tl-bl");
49648     },
49649
49650     handleHdOver : function(e){
49651         var hd = this.findHeaderCell(e.getTarget());
49652         if(hd && !this.headersDisabled){
49653             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
49654                this.fly(hd).addClass("x-grid-hd-over");
49655             }
49656         }
49657     },
49658
49659     handleHdOut : function(e){
49660         var hd = this.findHeaderCell(e.getTarget());
49661         if(hd){
49662             this.fly(hd).removeClass("x-grid-hd-over");
49663         }
49664     },
49665
49666     handleSplitDblClick : function(e, t){
49667         var i = this.getCellIndex(t);
49668         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
49669             this.autoSizeColumn(i, true);
49670             this.layout();
49671         }
49672     },
49673
49674     render : function(){
49675
49676         var cm = this.cm;
49677         var colCount = cm.getColumnCount();
49678
49679         if(this.grid.monitorWindowResize === true){
49680             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
49681         }
49682         var header = this.renderHeaders();
49683         var body = this.templates.body.apply({rows:""});
49684         var html = this.templates.master.apply({
49685             lockedBody: body,
49686             body: body,
49687             lockedHeader: header[0],
49688             header: header[1]
49689         });
49690
49691         //this.updateColumns();
49692
49693         this.grid.getGridEl().dom.innerHTML = html;
49694
49695         this.initElements();
49696         
49697         // a kludge to fix the random scolling effect in webkit
49698         this.el.on("scroll", function() {
49699             this.el.dom.scrollTop=0; // hopefully not recursive..
49700         },this);
49701
49702         this.scroller.on("scroll", this.handleScroll, this);
49703         this.lockedBody.on("mousewheel", this.handleWheel, this);
49704         this.mainBody.on("mousewheel", this.handleWheel, this);
49705
49706         this.mainHd.on("mouseover", this.handleHdOver, this);
49707         this.mainHd.on("mouseout", this.handleHdOut, this);
49708         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
49709                 {delegate: "."+this.splitClass});
49710
49711         this.lockedHd.on("mouseover", this.handleHdOver, this);
49712         this.lockedHd.on("mouseout", this.handleHdOut, this);
49713         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
49714                 {delegate: "."+this.splitClass});
49715
49716         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
49717             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49718         }
49719
49720         this.updateSplitters();
49721
49722         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
49723             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49724             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
49725         }
49726
49727         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
49728             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
49729             this.hmenu.add(
49730                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
49731                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
49732             );
49733             if(this.grid.enableColLock !== false){
49734                 this.hmenu.add('-',
49735                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
49736                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
49737                 );
49738             }
49739             if(this.grid.enableColumnHide !== false){
49740
49741                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
49742                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
49743                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
49744
49745                 this.hmenu.add('-',
49746                     {id:"columns", text: this.columnsText, menu: this.colMenu}
49747                 );
49748             }
49749             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
49750
49751             this.grid.on("headercontextmenu", this.handleHdCtx, this);
49752         }
49753
49754         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
49755             this.dd = new Roo.grid.GridDragZone(this.grid, {
49756                 ddGroup : this.grid.ddGroup || 'GridDD'
49757             });
49758         }
49759
49760         /*
49761         for(var i = 0; i < colCount; i++){
49762             if(cm.isHidden(i)){
49763                 this.hideColumn(i);
49764             }
49765             if(cm.config[i].align){
49766                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
49767                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
49768             }
49769         }*/
49770         
49771         this.updateHeaderSortState();
49772
49773         this.beforeInitialResize();
49774         this.layout(true);
49775
49776         // two part rendering gives faster view to the user
49777         this.renderPhase2.defer(1, this);
49778     },
49779
49780     renderPhase2 : function(){
49781         // render the rows now
49782         this.refresh();
49783         if(this.grid.autoSizeColumns){
49784             this.autoSizeColumns();
49785         }
49786     },
49787
49788     beforeInitialResize : function(){
49789
49790     },
49791
49792     onColumnSplitterMoved : function(i, w){
49793         this.userResized = true;
49794         var cm = this.grid.colModel;
49795         cm.setColumnWidth(i, w, true);
49796         var cid = cm.getColumnId(i);
49797         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49798         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
49799         this.updateSplitters();
49800         this.layout();
49801         this.grid.fireEvent("columnresize", i, w);
49802     },
49803
49804     syncRowHeights : function(startIndex, endIndex){
49805         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
49806             startIndex = startIndex || 0;
49807             var mrows = this.getBodyTable().rows;
49808             var lrows = this.getLockedTable().rows;
49809             var len = mrows.length-1;
49810             endIndex = Math.min(endIndex || len, len);
49811             for(var i = startIndex; i <= endIndex; i++){
49812                 var m = mrows[i], l = lrows[i];
49813                 var h = Math.max(m.offsetHeight, l.offsetHeight);
49814                 m.style.height = l.style.height = h + "px";
49815             }
49816         }
49817     },
49818
49819     layout : function(initialRender, is2ndPass){
49820         var g = this.grid;
49821         var auto = g.autoHeight;
49822         var scrollOffset = 16;
49823         var c = g.getGridEl(), cm = this.cm,
49824                 expandCol = g.autoExpandColumn,
49825                 gv = this;
49826         //c.beginMeasure();
49827
49828         if(!c.dom.offsetWidth){ // display:none?
49829             if(initialRender){
49830                 this.lockedWrap.show();
49831                 this.mainWrap.show();
49832             }
49833             return;
49834         }
49835
49836         var hasLock = this.cm.isLocked(0);
49837
49838         var tbh = this.headerPanel.getHeight();
49839         var bbh = this.footerPanel.getHeight();
49840
49841         if(auto){
49842             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
49843             var newHeight = ch + c.getBorderWidth("tb");
49844             if(g.maxHeight){
49845                 newHeight = Math.min(g.maxHeight, newHeight);
49846             }
49847             c.setHeight(newHeight);
49848         }
49849
49850         if(g.autoWidth){
49851             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
49852         }
49853
49854         var s = this.scroller;
49855
49856         var csize = c.getSize(true);
49857
49858         this.el.setSize(csize.width, csize.height);
49859
49860         this.headerPanel.setWidth(csize.width);
49861         this.footerPanel.setWidth(csize.width);
49862
49863         var hdHeight = this.mainHd.getHeight();
49864         var vw = csize.width;
49865         var vh = csize.height - (tbh + bbh);
49866
49867         s.setSize(vw, vh);
49868
49869         var bt = this.getBodyTable();
49870         var ltWidth = hasLock ?
49871                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
49872
49873         var scrollHeight = bt.offsetHeight;
49874         var scrollWidth = ltWidth + bt.offsetWidth;
49875         var vscroll = false, hscroll = false;
49876
49877         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
49878
49879         var lw = this.lockedWrap, mw = this.mainWrap;
49880         var lb = this.lockedBody, mb = this.mainBody;
49881
49882         setTimeout(function(){
49883             var t = s.dom.offsetTop;
49884             var w = s.dom.clientWidth,
49885                 h = s.dom.clientHeight;
49886
49887             lw.setTop(t);
49888             lw.setSize(ltWidth, h);
49889
49890             mw.setLeftTop(ltWidth, t);
49891             mw.setSize(w-ltWidth, h);
49892
49893             lb.setHeight(h-hdHeight);
49894             mb.setHeight(h-hdHeight);
49895
49896             if(is2ndPass !== true && !gv.userResized && expandCol){
49897                 // high speed resize without full column calculation
49898                 
49899                 var ci = cm.getIndexById(expandCol);
49900                 if (ci < 0) {
49901                     ci = cm.findColumnIndex(expandCol);
49902                 }
49903                 ci = Math.max(0, ci); // make sure it's got at least the first col.
49904                 var expandId = cm.getColumnId(ci);
49905                 var  tw = cm.getTotalWidth(false);
49906                 var currentWidth = cm.getColumnWidth(ci);
49907                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
49908                 if(currentWidth != cw){
49909                     cm.setColumnWidth(ci, cw, true);
49910                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49911                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
49912                     gv.updateSplitters();
49913                     gv.layout(false, true);
49914                 }
49915             }
49916
49917             if(initialRender){
49918                 lw.show();
49919                 mw.show();
49920             }
49921             //c.endMeasure();
49922         }, 10);
49923     },
49924
49925     onWindowResize : function(){
49926         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
49927             return;
49928         }
49929         this.layout();
49930     },
49931
49932     appendFooter : function(parentEl){
49933         return null;
49934     },
49935
49936     sortAscText : "Sort Ascending",
49937     sortDescText : "Sort Descending",
49938     lockText : "Lock Column",
49939     unlockText : "Unlock Column",
49940     columnsText : "Columns"
49941 });
49942
49943
49944 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
49945     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
49946     this.proxy.el.addClass('x-grid3-col-dd');
49947 };
49948
49949 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
49950     handleMouseDown : function(e){
49951
49952     },
49953
49954     callHandleMouseDown : function(e){
49955         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
49956     }
49957 });
49958 /*
49959  * Based on:
49960  * Ext JS Library 1.1.1
49961  * Copyright(c) 2006-2007, Ext JS, LLC.
49962  *
49963  * Originally Released Under LGPL - original licence link has changed is not relivant.
49964  *
49965  * Fork - LGPL
49966  * <script type="text/javascript">
49967  */
49968  
49969 // private
49970 // This is a support class used internally by the Grid components
49971 Roo.grid.SplitDragZone = function(grid, hd, hd2){
49972     this.grid = grid;
49973     this.view = grid.getView();
49974     this.proxy = this.view.resizeProxy;
49975     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
49976         "gridSplitters" + this.grid.getGridEl().id, {
49977         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
49978     });
49979     this.setHandleElId(Roo.id(hd));
49980     this.setOuterHandleElId(Roo.id(hd2));
49981     this.scroll = false;
49982 };
49983 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
49984     fly: Roo.Element.fly,
49985
49986     b4StartDrag : function(x, y){
49987         this.view.headersDisabled = true;
49988         this.proxy.setHeight(this.view.mainWrap.getHeight());
49989         var w = this.cm.getColumnWidth(this.cellIndex);
49990         var minw = Math.max(w-this.grid.minColumnWidth, 0);
49991         this.resetConstraints();
49992         this.setXConstraint(minw, 1000);
49993         this.setYConstraint(0, 0);
49994         this.minX = x - minw;
49995         this.maxX = x + 1000;
49996         this.startPos = x;
49997         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
49998     },
49999
50000
50001     handleMouseDown : function(e){
50002         ev = Roo.EventObject.setEvent(e);
50003         var t = this.fly(ev.getTarget());
50004         if(t.hasClass("x-grid-split")){
50005             this.cellIndex = this.view.getCellIndex(t.dom);
50006             this.split = t.dom;
50007             this.cm = this.grid.colModel;
50008             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
50009                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
50010             }
50011         }
50012     },
50013
50014     endDrag : function(e){
50015         this.view.headersDisabled = false;
50016         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
50017         var diff = endX - this.startPos;
50018         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
50019     },
50020
50021     autoOffset : function(){
50022         this.setDelta(0,0);
50023     }
50024 });/*
50025  * Based on:
50026  * Ext JS Library 1.1.1
50027  * Copyright(c) 2006-2007, Ext JS, LLC.
50028  *
50029  * Originally Released Under LGPL - original licence link has changed is not relivant.
50030  *
50031  * Fork - LGPL
50032  * <script type="text/javascript">
50033  */
50034  
50035 // private
50036 // This is a support class used internally by the Grid components
50037 Roo.grid.GridDragZone = function(grid, config){
50038     this.view = grid.getView();
50039     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
50040     if(this.view.lockedBody){
50041         this.setHandleElId(Roo.id(this.view.mainBody.dom));
50042         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
50043     }
50044     this.scroll = false;
50045     this.grid = grid;
50046     this.ddel = document.createElement('div');
50047     this.ddel.className = 'x-grid-dd-wrap';
50048 };
50049
50050 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
50051     ddGroup : "GridDD",
50052
50053     getDragData : function(e){
50054         var t = Roo.lib.Event.getTarget(e);
50055         var rowIndex = this.view.findRowIndex(t);
50056         if(rowIndex !== false){
50057             var sm = this.grid.selModel;
50058             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
50059               //  sm.mouseDown(e, t);
50060             //}
50061             if (e.hasModifier()){
50062                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
50063             }
50064             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
50065         }
50066         return false;
50067     },
50068
50069     onInitDrag : function(e){
50070         var data = this.dragData;
50071         this.ddel.innerHTML = this.grid.getDragDropText();
50072         this.proxy.update(this.ddel);
50073         // fire start drag?
50074     },
50075
50076     afterRepair : function(){
50077         this.dragging = false;
50078     },
50079
50080     getRepairXY : function(e, data){
50081         return false;
50082     },
50083
50084     onEndDrag : function(data, e){
50085         // fire end drag?
50086     },
50087
50088     onValidDrop : function(dd, e, id){
50089         // fire drag drop?
50090         this.hideProxy();
50091     },
50092
50093     beforeInvalidDrop : function(e, id){
50094
50095     }
50096 });/*
50097  * Based on:
50098  * Ext JS Library 1.1.1
50099  * Copyright(c) 2006-2007, Ext JS, LLC.
50100  *
50101  * Originally Released Under LGPL - original licence link has changed is not relivant.
50102  *
50103  * Fork - LGPL
50104  * <script type="text/javascript">
50105  */
50106  
50107
50108 /**
50109  * @class Roo.grid.ColumnModel
50110  * @extends Roo.util.Observable
50111  * This is the default implementation of a ColumnModel used by the Grid. It defines
50112  * the columns in the grid.
50113  * <br>Usage:<br>
50114  <pre><code>
50115  var colModel = new Roo.grid.ColumnModel([
50116         {header: "Ticker", width: 60, sortable: true, locked: true},
50117         {header: "Company Name", width: 150, sortable: true},
50118         {header: "Market Cap.", width: 100, sortable: true},
50119         {header: "$ Sales", width: 100, sortable: true, renderer: money},
50120         {header: "Employees", width: 100, sortable: true, resizable: false}
50121  ]);
50122  </code></pre>
50123  * <p>
50124  
50125  * The config options listed for this class are options which may appear in each
50126  * individual column definition.
50127  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
50128  * @constructor
50129  * @param {Object} config An Array of column config objects. See this class's
50130  * config objects for details.
50131 */
50132 Roo.grid.ColumnModel = function(config){
50133         /**
50134      * The config passed into the constructor
50135      */
50136     this.config = config;
50137     this.lookup = {};
50138
50139     // if no id, create one
50140     // if the column does not have a dataIndex mapping,
50141     // map it to the order it is in the config
50142     for(var i = 0, len = config.length; i < len; i++){
50143         var c = config[i];
50144         if(typeof c.dataIndex == "undefined"){
50145             c.dataIndex = i;
50146         }
50147         if(typeof c.renderer == "string"){
50148             c.renderer = Roo.util.Format[c.renderer];
50149         }
50150         if(typeof c.id == "undefined"){
50151             c.id = Roo.id();
50152         }
50153         if(c.editor && c.editor.xtype){
50154             c.editor  = Roo.factory(c.editor, Roo.grid);
50155         }
50156         if(c.editor && c.editor.isFormField){
50157             c.editor = new Roo.grid.GridEditor(c.editor);
50158         }
50159         this.lookup[c.id] = c;
50160     }
50161
50162     /**
50163      * The width of columns which have no width specified (defaults to 100)
50164      * @type Number
50165      */
50166     this.defaultWidth = 100;
50167
50168     /**
50169      * Default sortable of columns which have no sortable specified (defaults to false)
50170      * @type Boolean
50171      */
50172     this.defaultSortable = false;
50173
50174     this.addEvents({
50175         /**
50176              * @event widthchange
50177              * Fires when the width of a column changes.
50178              * @param {ColumnModel} this
50179              * @param {Number} columnIndex The column index
50180              * @param {Number} newWidth The new width
50181              */
50182             "widthchange": true,
50183         /**
50184              * @event headerchange
50185              * Fires when the text of a header changes.
50186              * @param {ColumnModel} this
50187              * @param {Number} columnIndex The column index
50188              * @param {Number} newText The new header text
50189              */
50190             "headerchange": true,
50191         /**
50192              * @event hiddenchange
50193              * Fires when a column is hidden or "unhidden".
50194              * @param {ColumnModel} this
50195              * @param {Number} columnIndex The column index
50196              * @param {Boolean} hidden true if hidden, false otherwise
50197              */
50198             "hiddenchange": true,
50199             /**
50200          * @event columnmoved
50201          * Fires when a column is moved.
50202          * @param {ColumnModel} this
50203          * @param {Number} oldIndex
50204          * @param {Number} newIndex
50205          */
50206         "columnmoved" : true,
50207         /**
50208          * @event columlockchange
50209          * Fires when a column's locked state is changed
50210          * @param {ColumnModel} this
50211          * @param {Number} colIndex
50212          * @param {Boolean} locked true if locked
50213          */
50214         "columnlockchange" : true
50215     });
50216     Roo.grid.ColumnModel.superclass.constructor.call(this);
50217 };
50218 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
50219     /**
50220      * @cfg {String} header The header text to display in the Grid view.
50221      */
50222     /**
50223      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
50224      * {@link Roo.data.Record} definition from which to draw the column's value. If not
50225      * specified, the column's index is used as an index into the Record's data Array.
50226      */
50227     /**
50228      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
50229      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
50230      */
50231     /**
50232      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
50233      * Defaults to the value of the {@link #defaultSortable} property.
50234      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
50235      */
50236     /**
50237      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
50238      */
50239     /**
50240      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
50241      */
50242     /**
50243      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
50244      */
50245     /**
50246      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
50247      */
50248     /**
50249      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
50250      * given the cell's data value. See {@link #setRenderer}. If not specified, the
50251      * default renderer uses the raw data value.
50252      */
50253        /**
50254      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
50255      */
50256     /**
50257      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
50258      */
50259
50260     /**
50261      * Returns the id of the column at the specified index.
50262      * @param {Number} index The column index
50263      * @return {String} the id
50264      */
50265     getColumnId : function(index){
50266         return this.config[index].id;
50267     },
50268
50269     /**
50270      * Returns the column for a specified id.
50271      * @param {String} id The column id
50272      * @return {Object} the column
50273      */
50274     getColumnById : function(id){
50275         return this.lookup[id];
50276     },
50277
50278     
50279     /**
50280      * Returns the column for a specified dataIndex.
50281      * @param {String} dataIndex The column dataIndex
50282      * @return {Object|Boolean} the column or false if not found
50283      */
50284     getColumnByDataIndex: function(dataIndex){
50285         var index = this.findColumnIndex(dataIndex);
50286         return index > -1 ? this.config[index] : false;
50287     },
50288     
50289     /**
50290      * Returns the index for a specified column id.
50291      * @param {String} id The column id
50292      * @return {Number} the index, or -1 if not found
50293      */
50294     getIndexById : function(id){
50295         for(var i = 0, len = this.config.length; i < len; i++){
50296             if(this.config[i].id == id){
50297                 return i;
50298             }
50299         }
50300         return -1;
50301     },
50302     
50303     /**
50304      * Returns the index for a specified column dataIndex.
50305      * @param {String} dataIndex The column dataIndex
50306      * @return {Number} the index, or -1 if not found
50307      */
50308     
50309     findColumnIndex : function(dataIndex){
50310         for(var i = 0, len = this.config.length; i < len; i++){
50311             if(this.config[i].dataIndex == dataIndex){
50312                 return i;
50313             }
50314         }
50315         return -1;
50316     },
50317     
50318     
50319     moveColumn : function(oldIndex, newIndex){
50320         var c = this.config[oldIndex];
50321         this.config.splice(oldIndex, 1);
50322         this.config.splice(newIndex, 0, c);
50323         this.dataMap = null;
50324         this.fireEvent("columnmoved", this, oldIndex, newIndex);
50325     },
50326
50327     isLocked : function(colIndex){
50328         return this.config[colIndex].locked === true;
50329     },
50330
50331     setLocked : function(colIndex, value, suppressEvent){
50332         if(this.isLocked(colIndex) == value){
50333             return;
50334         }
50335         this.config[colIndex].locked = value;
50336         if(!suppressEvent){
50337             this.fireEvent("columnlockchange", this, colIndex, value);
50338         }
50339     },
50340
50341     getTotalLockedWidth : function(){
50342         var totalWidth = 0;
50343         for(var i = 0; i < this.config.length; i++){
50344             if(this.isLocked(i) && !this.isHidden(i)){
50345                 this.totalWidth += this.getColumnWidth(i);
50346             }
50347         }
50348         return totalWidth;
50349     },
50350
50351     getLockedCount : function(){
50352         for(var i = 0, len = this.config.length; i < len; i++){
50353             if(!this.isLocked(i)){
50354                 return i;
50355             }
50356         }
50357     },
50358
50359     /**
50360      * Returns the number of columns.
50361      * @return {Number}
50362      */
50363     getColumnCount : function(visibleOnly){
50364         if(visibleOnly === true){
50365             var c = 0;
50366             for(var i = 0, len = this.config.length; i < len; i++){
50367                 if(!this.isHidden(i)){
50368                     c++;
50369                 }
50370             }
50371             return c;
50372         }
50373         return this.config.length;
50374     },
50375
50376     /**
50377      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
50378      * @param {Function} fn
50379      * @param {Object} scope (optional)
50380      * @return {Array} result
50381      */
50382     getColumnsBy : function(fn, scope){
50383         var r = [];
50384         for(var i = 0, len = this.config.length; i < len; i++){
50385             var c = this.config[i];
50386             if(fn.call(scope||this, c, i) === true){
50387                 r[r.length] = c;
50388             }
50389         }
50390         return r;
50391     },
50392
50393     /**
50394      * Returns true if the specified column is sortable.
50395      * @param {Number} col The column index
50396      * @return {Boolean}
50397      */
50398     isSortable : function(col){
50399         if(typeof this.config[col].sortable == "undefined"){
50400             return this.defaultSortable;
50401         }
50402         return this.config[col].sortable;
50403     },
50404
50405     /**
50406      * Returns the rendering (formatting) function defined for the column.
50407      * @param {Number} col The column index.
50408      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
50409      */
50410     getRenderer : function(col){
50411         if(!this.config[col].renderer){
50412             return Roo.grid.ColumnModel.defaultRenderer;
50413         }
50414         return this.config[col].renderer;
50415     },
50416
50417     /**
50418      * Sets the rendering (formatting) function for a column.
50419      * @param {Number} col The column index
50420      * @param {Function} fn The function to use to process the cell's raw data
50421      * to return HTML markup for the grid view. The render function is called with
50422      * the following parameters:<ul>
50423      * <li>Data value.</li>
50424      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
50425      * <li>css A CSS style string to apply to the table cell.</li>
50426      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
50427      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
50428      * <li>Row index</li>
50429      * <li>Column index</li>
50430      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
50431      */
50432     setRenderer : function(col, fn){
50433         this.config[col].renderer = fn;
50434     },
50435
50436     /**
50437      * Returns the width for the specified column.
50438      * @param {Number} col The column index
50439      * @return {Number}
50440      */
50441     getColumnWidth : function(col){
50442         return this.config[col].width * 1 || this.defaultWidth;
50443     },
50444
50445     /**
50446      * Sets the width for a column.
50447      * @param {Number} col The column index
50448      * @param {Number} width The new width
50449      */
50450     setColumnWidth : function(col, width, suppressEvent){
50451         this.config[col].width = width;
50452         this.totalWidth = null;
50453         if(!suppressEvent){
50454              this.fireEvent("widthchange", this, col, width);
50455         }
50456     },
50457
50458     /**
50459      * Returns the total width of all columns.
50460      * @param {Boolean} includeHidden True to include hidden column widths
50461      * @return {Number}
50462      */
50463     getTotalWidth : function(includeHidden){
50464         if(!this.totalWidth){
50465             this.totalWidth = 0;
50466             for(var i = 0, len = this.config.length; i < len; i++){
50467                 if(includeHidden || !this.isHidden(i)){
50468                     this.totalWidth += this.getColumnWidth(i);
50469                 }
50470             }
50471         }
50472         return this.totalWidth;
50473     },
50474
50475     /**
50476      * Returns the header for the specified column.
50477      * @param {Number} col The column index
50478      * @return {String}
50479      */
50480     getColumnHeader : function(col){
50481         return this.config[col].header;
50482     },
50483
50484     /**
50485      * Sets the header for a column.
50486      * @param {Number} col The column index
50487      * @param {String} header The new header
50488      */
50489     setColumnHeader : function(col, header){
50490         this.config[col].header = header;
50491         this.fireEvent("headerchange", this, col, header);
50492     },
50493
50494     /**
50495      * Returns the tooltip for the specified column.
50496      * @param {Number} col The column index
50497      * @return {String}
50498      */
50499     getColumnTooltip : function(col){
50500             return this.config[col].tooltip;
50501     },
50502     /**
50503      * Sets the tooltip for a column.
50504      * @param {Number} col The column index
50505      * @param {String} tooltip The new tooltip
50506      */
50507     setColumnTooltip : function(col, tooltip){
50508             this.config[col].tooltip = tooltip;
50509     },
50510
50511     /**
50512      * Returns the dataIndex for the specified column.
50513      * @param {Number} col The column index
50514      * @return {Number}
50515      */
50516     getDataIndex : function(col){
50517         return this.config[col].dataIndex;
50518     },
50519
50520     /**
50521      * Sets the dataIndex for a column.
50522      * @param {Number} col The column index
50523      * @param {Number} dataIndex The new dataIndex
50524      */
50525     setDataIndex : function(col, dataIndex){
50526         this.config[col].dataIndex = dataIndex;
50527     },
50528
50529     
50530     
50531     /**
50532      * Returns true if the cell is editable.
50533      * @param {Number} colIndex The column index
50534      * @param {Number} rowIndex The row index
50535      * @return {Boolean}
50536      */
50537     isCellEditable : function(colIndex, rowIndex){
50538         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
50539     },
50540
50541     /**
50542      * Returns the editor defined for the cell/column.
50543      * return false or null to disable editing.
50544      * @param {Number} colIndex The column index
50545      * @param {Number} rowIndex The row index
50546      * @return {Object}
50547      */
50548     getCellEditor : function(colIndex, rowIndex){
50549         return this.config[colIndex].editor;
50550     },
50551
50552     /**
50553      * Sets if a column is editable.
50554      * @param {Number} col The column index
50555      * @param {Boolean} editable True if the column is editable
50556      */
50557     setEditable : function(col, editable){
50558         this.config[col].editable = editable;
50559     },
50560
50561
50562     /**
50563      * Returns true if the column is hidden.
50564      * @param {Number} colIndex The column index
50565      * @return {Boolean}
50566      */
50567     isHidden : function(colIndex){
50568         return this.config[colIndex].hidden;
50569     },
50570
50571
50572     /**
50573      * Returns true if the column width cannot be changed
50574      */
50575     isFixed : function(colIndex){
50576         return this.config[colIndex].fixed;
50577     },
50578
50579     /**
50580      * Returns true if the column can be resized
50581      * @return {Boolean}
50582      */
50583     isResizable : function(colIndex){
50584         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
50585     },
50586     /**
50587      * Sets if a column is hidden.
50588      * @param {Number} colIndex The column index
50589      * @param {Boolean} hidden True if the column is hidden
50590      */
50591     setHidden : function(colIndex, hidden){
50592         this.config[colIndex].hidden = hidden;
50593         this.totalWidth = null;
50594         this.fireEvent("hiddenchange", this, colIndex, hidden);
50595     },
50596
50597     /**
50598      * Sets the editor for a column.
50599      * @param {Number} col The column index
50600      * @param {Object} editor The editor object
50601      */
50602     setEditor : function(col, editor){
50603         this.config[col].editor = editor;
50604     }
50605 });
50606
50607 Roo.grid.ColumnModel.defaultRenderer = function(value){
50608         if(typeof value == "string" && value.length < 1){
50609             return "&#160;";
50610         }
50611         return value;
50612 };
50613
50614 // Alias for backwards compatibility
50615 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
50616 /*
50617  * Based on:
50618  * Ext JS Library 1.1.1
50619  * Copyright(c) 2006-2007, Ext JS, LLC.
50620  *
50621  * Originally Released Under LGPL - original licence link has changed is not relivant.
50622  *
50623  * Fork - LGPL
50624  * <script type="text/javascript">
50625  */
50626
50627 /**
50628  * @class Roo.grid.AbstractSelectionModel
50629  * @extends Roo.util.Observable
50630  * Abstract base class for grid SelectionModels.  It provides the interface that should be
50631  * implemented by descendant classes.  This class should not be directly instantiated.
50632  * @constructor
50633  */
50634 Roo.grid.AbstractSelectionModel = function(){
50635     this.locked = false;
50636     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
50637 };
50638
50639 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
50640     /** @ignore Called by the grid automatically. Do not call directly. */
50641     init : function(grid){
50642         this.grid = grid;
50643         this.initEvents();
50644     },
50645
50646     /**
50647      * Locks the selections.
50648      */
50649     lock : function(){
50650         this.locked = true;
50651     },
50652
50653     /**
50654      * Unlocks the selections.
50655      */
50656     unlock : function(){
50657         this.locked = false;
50658     },
50659
50660     /**
50661      * Returns true if the selections are locked.
50662      * @return {Boolean}
50663      */
50664     isLocked : function(){
50665         return this.locked;
50666     }
50667 });/*
50668  * Based on:
50669  * Ext JS Library 1.1.1
50670  * Copyright(c) 2006-2007, Ext JS, LLC.
50671  *
50672  * Originally Released Under LGPL - original licence link has changed is not relivant.
50673  *
50674  * Fork - LGPL
50675  * <script type="text/javascript">
50676  */
50677 /**
50678  * @extends Roo.grid.AbstractSelectionModel
50679  * @class Roo.grid.RowSelectionModel
50680  * The default SelectionModel used by {@link Roo.grid.Grid}.
50681  * It supports multiple selections and keyboard selection/navigation. 
50682  * @constructor
50683  * @param {Object} config
50684  */
50685 Roo.grid.RowSelectionModel = function(config){
50686     Roo.apply(this, config);
50687     this.selections = new Roo.util.MixedCollection(false, function(o){
50688         return o.id;
50689     });
50690
50691     this.last = false;
50692     this.lastActive = false;
50693
50694     this.addEvents({
50695         /**
50696              * @event selectionchange
50697              * Fires when the selection changes
50698              * @param {SelectionModel} this
50699              */
50700             "selectionchange" : true,
50701         /**
50702              * @event afterselectionchange
50703              * Fires after the selection changes (eg. by key press or clicking)
50704              * @param {SelectionModel} this
50705              */
50706             "afterselectionchange" : true,
50707         /**
50708              * @event beforerowselect
50709              * Fires when a row is selected being selected, return false to cancel.
50710              * @param {SelectionModel} this
50711              * @param {Number} rowIndex The selected index
50712              * @param {Boolean} keepExisting False if other selections will be cleared
50713              */
50714             "beforerowselect" : true,
50715         /**
50716              * @event rowselect
50717              * Fires when a row is selected.
50718              * @param {SelectionModel} this
50719              * @param {Number} rowIndex The selected index
50720              * @param {Roo.data.Record} r The record
50721              */
50722             "rowselect" : true,
50723         /**
50724              * @event rowdeselect
50725              * Fires when a row is deselected.
50726              * @param {SelectionModel} this
50727              * @param {Number} rowIndex The selected index
50728              */
50729         "rowdeselect" : true
50730     });
50731     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
50732     this.locked = false;
50733 };
50734
50735 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
50736     /**
50737      * @cfg {Boolean} singleSelect
50738      * True to allow selection of only one row at a time (defaults to false)
50739      */
50740     singleSelect : false,
50741
50742     // private
50743     initEvents : function(){
50744
50745         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
50746             this.grid.on("mousedown", this.handleMouseDown, this);
50747         }else{ // allow click to work like normal
50748             this.grid.on("rowclick", this.handleDragableRowClick, this);
50749         }
50750
50751         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
50752             "up" : function(e){
50753                 if(!e.shiftKey){
50754                     this.selectPrevious(e.shiftKey);
50755                 }else if(this.last !== false && this.lastActive !== false){
50756                     var last = this.last;
50757                     this.selectRange(this.last,  this.lastActive-1);
50758                     this.grid.getView().focusRow(this.lastActive);
50759                     if(last !== false){
50760                         this.last = last;
50761                     }
50762                 }else{
50763                     this.selectFirstRow();
50764                 }
50765                 this.fireEvent("afterselectionchange", this);
50766             },
50767             "down" : function(e){
50768                 if(!e.shiftKey){
50769                     this.selectNext(e.shiftKey);
50770                 }else if(this.last !== false && this.lastActive !== false){
50771                     var last = this.last;
50772                     this.selectRange(this.last,  this.lastActive+1);
50773                     this.grid.getView().focusRow(this.lastActive);
50774                     if(last !== false){
50775                         this.last = last;
50776                     }
50777                 }else{
50778                     this.selectFirstRow();
50779                 }
50780                 this.fireEvent("afterselectionchange", this);
50781             },
50782             scope: this
50783         });
50784
50785         var view = this.grid.view;
50786         view.on("refresh", this.onRefresh, this);
50787         view.on("rowupdated", this.onRowUpdated, this);
50788         view.on("rowremoved", this.onRemove, this);
50789     },
50790
50791     // private
50792     onRefresh : function(){
50793         var ds = this.grid.dataSource, i, v = this.grid.view;
50794         var s = this.selections;
50795         s.each(function(r){
50796             if((i = ds.indexOfId(r.id)) != -1){
50797                 v.onRowSelect(i);
50798             }else{
50799                 s.remove(r);
50800             }
50801         });
50802     },
50803
50804     // private
50805     onRemove : function(v, index, r){
50806         this.selections.remove(r);
50807     },
50808
50809     // private
50810     onRowUpdated : function(v, index, r){
50811         if(this.isSelected(r)){
50812             v.onRowSelect(index);
50813         }
50814     },
50815
50816     /**
50817      * Select records.
50818      * @param {Array} records The records to select
50819      * @param {Boolean} keepExisting (optional) True to keep existing selections
50820      */
50821     selectRecords : function(records, keepExisting){
50822         if(!keepExisting){
50823             this.clearSelections();
50824         }
50825         var ds = this.grid.dataSource;
50826         for(var i = 0, len = records.length; i < len; i++){
50827             this.selectRow(ds.indexOf(records[i]), true);
50828         }
50829     },
50830
50831     /**
50832      * Gets the number of selected rows.
50833      * @return {Number}
50834      */
50835     getCount : function(){
50836         return this.selections.length;
50837     },
50838
50839     /**
50840      * Selects the first row in the grid.
50841      */
50842     selectFirstRow : function(){
50843         this.selectRow(0);
50844     },
50845
50846     /**
50847      * Select the last row.
50848      * @param {Boolean} keepExisting (optional) True to keep existing selections
50849      */
50850     selectLastRow : function(keepExisting){
50851         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
50852     },
50853
50854     /**
50855      * Selects the row immediately following the last selected row.
50856      * @param {Boolean} keepExisting (optional) True to keep existing selections
50857      */
50858     selectNext : function(keepExisting){
50859         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
50860             this.selectRow(this.last+1, keepExisting);
50861             this.grid.getView().focusRow(this.last);
50862         }
50863     },
50864
50865     /**
50866      * Selects the row that precedes the last selected row.
50867      * @param {Boolean} keepExisting (optional) True to keep existing selections
50868      */
50869     selectPrevious : function(keepExisting){
50870         if(this.last){
50871             this.selectRow(this.last-1, keepExisting);
50872             this.grid.getView().focusRow(this.last);
50873         }
50874     },
50875
50876     /**
50877      * Returns the selected records
50878      * @return {Array} Array of selected records
50879      */
50880     getSelections : function(){
50881         return [].concat(this.selections.items);
50882     },
50883
50884     /**
50885      * Returns the first selected record.
50886      * @return {Record}
50887      */
50888     getSelected : function(){
50889         return this.selections.itemAt(0);
50890     },
50891
50892
50893     /**
50894      * Clears all selections.
50895      */
50896     clearSelections : function(fast){
50897         if(this.locked) return;
50898         if(fast !== true){
50899             var ds = this.grid.dataSource;
50900             var s = this.selections;
50901             s.each(function(r){
50902                 this.deselectRow(ds.indexOfId(r.id));
50903             }, this);
50904             s.clear();
50905         }else{
50906             this.selections.clear();
50907         }
50908         this.last = false;
50909     },
50910
50911
50912     /**
50913      * Selects all rows.
50914      */
50915     selectAll : function(){
50916         if(this.locked) return;
50917         this.selections.clear();
50918         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
50919             this.selectRow(i, true);
50920         }
50921     },
50922
50923     /**
50924      * Returns True if there is a selection.
50925      * @return {Boolean}
50926      */
50927     hasSelection : function(){
50928         return this.selections.length > 0;
50929     },
50930
50931     /**
50932      * Returns True if the specified row is selected.
50933      * @param {Number/Record} record The record or index of the record to check
50934      * @return {Boolean}
50935      */
50936     isSelected : function(index){
50937         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
50938         return (r && this.selections.key(r.id) ? true : false);
50939     },
50940
50941     /**
50942      * Returns True if the specified record id is selected.
50943      * @param {String} id The id of record to check
50944      * @return {Boolean}
50945      */
50946     isIdSelected : function(id){
50947         return (this.selections.key(id) ? true : false);
50948     },
50949
50950     // private
50951     handleMouseDown : function(e, t){
50952         var view = this.grid.getView(), rowIndex;
50953         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
50954             return;
50955         };
50956         if(e.shiftKey && this.last !== false){
50957             var last = this.last;
50958             this.selectRange(last, rowIndex, e.ctrlKey);
50959             this.last = last; // reset the last
50960             view.focusRow(rowIndex);
50961         }else{
50962             var isSelected = this.isSelected(rowIndex);
50963             if(e.button !== 0 && isSelected){
50964                 view.focusRow(rowIndex);
50965             }else if(e.ctrlKey && isSelected){
50966                 this.deselectRow(rowIndex);
50967             }else if(!isSelected){
50968                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
50969                 view.focusRow(rowIndex);
50970             }
50971         }
50972         this.fireEvent("afterselectionchange", this);
50973     },
50974     // private
50975     handleDragableRowClick :  function(grid, rowIndex, e) 
50976     {
50977         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
50978             this.selectRow(rowIndex, false);
50979             grid.view.focusRow(rowIndex);
50980              this.fireEvent("afterselectionchange", this);
50981         }
50982     },
50983     
50984     /**
50985      * Selects multiple rows.
50986      * @param {Array} rows Array of the indexes of the row to select
50987      * @param {Boolean} keepExisting (optional) True to keep existing selections
50988      */
50989     selectRows : function(rows, keepExisting){
50990         if(!keepExisting){
50991             this.clearSelections();
50992         }
50993         for(var i = 0, len = rows.length; i < len; i++){
50994             this.selectRow(rows[i], true);
50995         }
50996     },
50997
50998     /**
50999      * Selects a range of rows. All rows in between startRow and endRow are also selected.
51000      * @param {Number} startRow The index of the first row in the range
51001      * @param {Number} endRow The index of the last row in the range
51002      * @param {Boolean} keepExisting (optional) True to retain existing selections
51003      */
51004     selectRange : function(startRow, endRow, keepExisting){
51005         if(this.locked) return;
51006         if(!keepExisting){
51007             this.clearSelections();
51008         }
51009         if(startRow <= endRow){
51010             for(var i = startRow; i <= endRow; i++){
51011                 this.selectRow(i, true);
51012             }
51013         }else{
51014             for(var i = startRow; i >= endRow; i--){
51015                 this.selectRow(i, true);
51016             }
51017         }
51018     },
51019
51020     /**
51021      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
51022      * @param {Number} startRow The index of the first row in the range
51023      * @param {Number} endRow The index of the last row in the range
51024      */
51025     deselectRange : function(startRow, endRow, preventViewNotify){
51026         if(this.locked) return;
51027         for(var i = startRow; i <= endRow; i++){
51028             this.deselectRow(i, preventViewNotify);
51029         }
51030     },
51031
51032     /**
51033      * Selects a row.
51034      * @param {Number} row The index of the row to select
51035      * @param {Boolean} keepExisting (optional) True to keep existing selections
51036      */
51037     selectRow : function(index, keepExisting, preventViewNotify){
51038         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
51039         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
51040             if(!keepExisting || this.singleSelect){
51041                 this.clearSelections();
51042             }
51043             var r = this.grid.dataSource.getAt(index);
51044             this.selections.add(r);
51045             this.last = this.lastActive = index;
51046             if(!preventViewNotify){
51047                 this.grid.getView().onRowSelect(index);
51048             }
51049             this.fireEvent("rowselect", this, index, r);
51050             this.fireEvent("selectionchange", this);
51051         }
51052     },
51053
51054     /**
51055      * Deselects a row.
51056      * @param {Number} row The index of the row to deselect
51057      */
51058     deselectRow : function(index, preventViewNotify){
51059         if(this.locked) return;
51060         if(this.last == index){
51061             this.last = false;
51062         }
51063         if(this.lastActive == index){
51064             this.lastActive = false;
51065         }
51066         var r = this.grid.dataSource.getAt(index);
51067         this.selections.remove(r);
51068         if(!preventViewNotify){
51069             this.grid.getView().onRowDeselect(index);
51070         }
51071         this.fireEvent("rowdeselect", this, index);
51072         this.fireEvent("selectionchange", this);
51073     },
51074
51075     // private
51076     restoreLast : function(){
51077         if(this._last){
51078             this.last = this._last;
51079         }
51080     },
51081
51082     // private
51083     acceptsNav : function(row, col, cm){
51084         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51085     },
51086
51087     // private
51088     onEditorKey : function(field, e){
51089         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51090         if(k == e.TAB){
51091             e.stopEvent();
51092             ed.completeEdit();
51093             if(e.shiftKey){
51094                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51095             }else{
51096                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51097             }
51098         }else if(k == e.ENTER && !e.ctrlKey){
51099             e.stopEvent();
51100             ed.completeEdit();
51101             if(e.shiftKey){
51102                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
51103             }else{
51104                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
51105             }
51106         }else if(k == e.ESC){
51107             ed.cancelEdit();
51108         }
51109         if(newCell){
51110             g.startEditing(newCell[0], newCell[1]);
51111         }
51112     }
51113 });/*
51114  * Based on:
51115  * Ext JS Library 1.1.1
51116  * Copyright(c) 2006-2007, Ext JS, LLC.
51117  *
51118  * Originally Released Under LGPL - original licence link has changed is not relivant.
51119  *
51120  * Fork - LGPL
51121  * <script type="text/javascript">
51122  */
51123 /**
51124  * @class Roo.grid.CellSelectionModel
51125  * @extends Roo.grid.AbstractSelectionModel
51126  * This class provides the basic implementation for cell selection in a grid.
51127  * @constructor
51128  * @param {Object} config The object containing the configuration of this model.
51129  */
51130 Roo.grid.CellSelectionModel = function(config){
51131     Roo.apply(this, config);
51132
51133     this.selection = null;
51134
51135     this.addEvents({
51136         /**
51137              * @event beforerowselect
51138              * Fires before a cell is selected.
51139              * @param {SelectionModel} this
51140              * @param {Number} rowIndex The selected row index
51141              * @param {Number} colIndex The selected cell index
51142              */
51143             "beforecellselect" : true,
51144         /**
51145              * @event cellselect
51146              * Fires when a cell is selected.
51147              * @param {SelectionModel} this
51148              * @param {Number} rowIndex The selected row index
51149              * @param {Number} colIndex The selected cell index
51150              */
51151             "cellselect" : true,
51152         /**
51153              * @event selectionchange
51154              * Fires when the active selection changes.
51155              * @param {SelectionModel} this
51156              * @param {Object} selection null for no selection or an object (o) with two properties
51157                 <ul>
51158                 <li>o.record: the record object for the row the selection is in</li>
51159                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
51160                 </ul>
51161              */
51162             "selectionchange" : true
51163     });
51164     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
51165 };
51166
51167 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
51168
51169     /** @ignore */
51170     initEvents : function(){
51171         this.grid.on("mousedown", this.handleMouseDown, this);
51172         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
51173         var view = this.grid.view;
51174         view.on("refresh", this.onViewChange, this);
51175         view.on("rowupdated", this.onRowUpdated, this);
51176         view.on("beforerowremoved", this.clearSelections, this);
51177         view.on("beforerowsinserted", this.clearSelections, this);
51178         if(this.grid.isEditor){
51179             this.grid.on("beforeedit", this.beforeEdit,  this);
51180         }
51181     },
51182
51183         //private
51184     beforeEdit : function(e){
51185         this.select(e.row, e.column, false, true, e.record);
51186     },
51187
51188         //private
51189     onRowUpdated : function(v, index, r){
51190         if(this.selection && this.selection.record == r){
51191             v.onCellSelect(index, this.selection.cell[1]);
51192         }
51193     },
51194
51195         //private
51196     onViewChange : function(){
51197         this.clearSelections(true);
51198     },
51199
51200         /**
51201          * Returns the currently selected cell,.
51202          * @return {Array} The selected cell (row, column) or null if none selected.
51203          */
51204     getSelectedCell : function(){
51205         return this.selection ? this.selection.cell : null;
51206     },
51207
51208     /**
51209      * Clears all selections.
51210      * @param {Boolean} true to prevent the gridview from being notified about the change.
51211      */
51212     clearSelections : function(preventNotify){
51213         var s = this.selection;
51214         if(s){
51215             if(preventNotify !== true){
51216                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
51217             }
51218             this.selection = null;
51219             this.fireEvent("selectionchange", this, null);
51220         }
51221     },
51222
51223     /**
51224      * Returns true if there is a selection.
51225      * @return {Boolean}
51226      */
51227     hasSelection : function(){
51228         return this.selection ? true : false;
51229     },
51230
51231     /** @ignore */
51232     handleMouseDown : function(e, t){
51233         var v = this.grid.getView();
51234         if(this.isLocked()){
51235             return;
51236         };
51237         var row = v.findRowIndex(t);
51238         var cell = v.findCellIndex(t);
51239         if(row !== false && cell !== false){
51240             this.select(row, cell);
51241         }
51242     },
51243
51244     /**
51245      * Selects a cell.
51246      * @param {Number} rowIndex
51247      * @param {Number} collIndex
51248      */
51249     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
51250         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
51251             this.clearSelections();
51252             r = r || this.grid.dataSource.getAt(rowIndex);
51253             this.selection = {
51254                 record : r,
51255                 cell : [rowIndex, colIndex]
51256             };
51257             if(!preventViewNotify){
51258                 var v = this.grid.getView();
51259                 v.onCellSelect(rowIndex, colIndex);
51260                 if(preventFocus !== true){
51261                     v.focusCell(rowIndex, colIndex);
51262                 }
51263             }
51264             this.fireEvent("cellselect", this, rowIndex, colIndex);
51265             this.fireEvent("selectionchange", this, this.selection);
51266         }
51267     },
51268
51269         //private
51270     isSelectable : function(rowIndex, colIndex, cm){
51271         return !cm.isHidden(colIndex);
51272     },
51273
51274     /** @ignore */
51275     handleKeyDown : function(e){
51276         //Roo.log('Cell Sel Model handleKeyDown');
51277         if(!e.isNavKeyPress()){
51278             return;
51279         }
51280         var g = this.grid, s = this.selection;
51281         if(!s){
51282             e.stopEvent();
51283             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
51284             if(cell){
51285                 this.select(cell[0], cell[1]);
51286             }
51287             return;
51288         }
51289         var sm = this;
51290         var walk = function(row, col, step){
51291             return g.walkCells(row, col, step, sm.isSelectable,  sm);
51292         };
51293         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
51294         var newCell;
51295
51296         switch(k){
51297             case e.TAB:
51298                 // handled by onEditorKey
51299                 if (g.isEditor && g.editing) {
51300                     return;
51301                 }
51302                 if(e.shiftKey){
51303                      newCell = walk(r, c-1, -1);
51304                 }else{
51305                      newCell = walk(r, c+1, 1);
51306                 }
51307              break;
51308              case e.DOWN:
51309                  newCell = walk(r+1, c, 1);
51310              break;
51311              case e.UP:
51312                  newCell = walk(r-1, c, -1);
51313              break;
51314              case e.RIGHT:
51315                  newCell = walk(r, c+1, 1);
51316              break;
51317              case e.LEFT:
51318                  newCell = walk(r, c-1, -1);
51319              break;
51320              case e.ENTER:
51321                  if(g.isEditor && !g.editing){
51322                     g.startEditing(r, c);
51323                     e.stopEvent();
51324                     return;
51325                 }
51326              break;
51327         };
51328         if(newCell){
51329             this.select(newCell[0], newCell[1]);
51330             e.stopEvent();
51331         }
51332     },
51333
51334     acceptsNav : function(row, col, cm){
51335         return !cm.isHidden(col) && cm.isCellEditable(col, row);
51336     },
51337     /**
51338      * Selects a cell.
51339      * @param {Number} field (not used) - as it's normally used as a listener
51340      * @param {Number} e - event - fake it by using
51341      *
51342      * var e = Roo.EventObjectImpl.prototype;
51343      * e.keyCode = e.TAB
51344      *
51345      * 
51346      */
51347     onEditorKey : function(field, e){
51348         
51349         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
51350         ///Roo.log('onEditorKey' + k);
51351         if (!ed) {
51352             
51353             
51354             
51355         }
51356         if(k == e.TAB){
51357             if(e.shiftKey){
51358                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
51359             }else{
51360                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51361             }
51362             
51363             e.stopEvent();
51364             
51365         }else if(k == e.ENTER &&  !e.ctrlKey){
51366             ed.completeEdit();
51367             e.stopEvent();
51368             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
51369         }else if(k == e.ESC){
51370             ed.cancelEdit();
51371         }
51372         
51373         
51374         if(newCell){
51375             //Roo.log('next cell after edit');
51376             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
51377         }
51378     }
51379 });/*
51380  * Based on:
51381  * Ext JS Library 1.1.1
51382  * Copyright(c) 2006-2007, Ext JS, LLC.
51383  *
51384  * Originally Released Under LGPL - original licence link has changed is not relivant.
51385  *
51386  * Fork - LGPL
51387  * <script type="text/javascript">
51388  */
51389  
51390 /**
51391  * @class Roo.grid.EditorGrid
51392  * @extends Roo.grid.Grid
51393  * Class for creating and editable grid.
51394  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
51395  * The container MUST have some type of size defined for the grid to fill. The container will be 
51396  * automatically set to position relative if it isn't already.
51397  * @param {Object} dataSource The data model to bind to
51398  * @param {Object} colModel The column model with info about this grid's columns
51399  */
51400 Roo.grid.EditorGrid = function(container, config){
51401     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
51402     this.getGridEl().addClass("xedit-grid");
51403
51404     if(!this.selModel){
51405         this.selModel = new Roo.grid.CellSelectionModel();
51406     }
51407
51408     this.activeEditor = null;
51409
51410         this.addEvents({
51411             /**
51412              * @event beforeedit
51413              * Fires before cell editing is triggered. The edit event object has the following properties <br />
51414              * <ul style="padding:5px;padding-left:16px;">
51415              * <li>grid - This grid</li>
51416              * <li>record - The record being edited</li>
51417              * <li>field - The field name being edited</li>
51418              * <li>value - The value for the field being edited.</li>
51419              * <li>row - The grid row index</li>
51420              * <li>column - The grid column index</li>
51421              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51422              * </ul>
51423              * @param {Object} e An edit event (see above for description)
51424              */
51425             "beforeedit" : true,
51426             /**
51427              * @event afteredit
51428              * Fires after a cell is edited. <br />
51429              * <ul style="padding:5px;padding-left:16px;">
51430              * <li>grid - This grid</li>
51431              * <li>record - The record being edited</li>
51432              * <li>field - The field name being edited</li>
51433              * <li>value - The value being set</li>
51434              * <li>originalValue - The original value for the field, before the edit.</li>
51435              * <li>row - The grid row index</li>
51436              * <li>column - The grid column index</li>
51437              * </ul>
51438              * @param {Object} e An edit event (see above for description)
51439              */
51440             "afteredit" : true,
51441             /**
51442              * @event validateedit
51443              * Fires after a cell is edited, but before the value is set in the record. 
51444          * You can use this to modify the value being set in the field, Return false
51445              * to cancel the change. The edit event object has the following properties <br />
51446              * <ul style="padding:5px;padding-left:16px;">
51447          * <li>editor - This editor</li>
51448              * <li>grid - This grid</li>
51449              * <li>record - The record being edited</li>
51450              * <li>field - The field name being edited</li>
51451              * <li>value - The value being set</li>
51452              * <li>originalValue - The original value for the field, before the edit.</li>
51453              * <li>row - The grid row index</li>
51454              * <li>column - The grid column index</li>
51455              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
51456              * </ul>
51457              * @param {Object} e An edit event (see above for description)
51458              */
51459             "validateedit" : true
51460         });
51461     this.on("bodyscroll", this.stopEditing,  this);
51462     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
51463 };
51464
51465 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
51466     /**
51467      * @cfg {Number} clicksToEdit
51468      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
51469      */
51470     clicksToEdit: 2,
51471
51472     // private
51473     isEditor : true,
51474     // private
51475     trackMouseOver: false, // causes very odd FF errors
51476
51477     onCellDblClick : function(g, row, col){
51478         this.startEditing(row, col);
51479     },
51480
51481     onEditComplete : function(ed, value, startValue){
51482         this.editing = false;
51483         this.activeEditor = null;
51484         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
51485         var r = ed.record;
51486         var field = this.colModel.getDataIndex(ed.col);
51487         var e = {
51488             grid: this,
51489             record: r,
51490             field: field,
51491             originalValue: startValue,
51492             value: value,
51493             row: ed.row,
51494             column: ed.col,
51495             cancel:false,
51496             editor: ed
51497         };
51498         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
51499         cell.show();
51500           
51501         if(String(value) !== String(startValue)){
51502             
51503             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
51504                 r.set(field, e.value);
51505                 // if we are dealing with a combo box..
51506                 // then we also set the 'name' colum to be the displayField
51507                 if (ed.field.displayField && ed.field.name) {
51508                     r.set(ed.field.name, ed.field.el.dom.value);
51509                 }
51510                 
51511                 delete e.cancel; //?? why!!!
51512                 this.fireEvent("afteredit", e);
51513             }
51514         } else {
51515             this.fireEvent("afteredit", e); // always fire it!
51516         }
51517         this.view.focusCell(ed.row, ed.col);
51518     },
51519
51520     /**
51521      * Starts editing the specified for the specified row/column
51522      * @param {Number} rowIndex
51523      * @param {Number} colIndex
51524      */
51525     startEditing : function(row, col){
51526         this.stopEditing();
51527         if(this.colModel.isCellEditable(col, row)){
51528             this.view.ensureVisible(row, col, true);
51529           
51530             var r = this.dataSource.getAt(row);
51531             var field = this.colModel.getDataIndex(col);
51532             var cell = Roo.get(this.view.getCell(row,col));
51533             var e = {
51534                 grid: this,
51535                 record: r,
51536                 field: field,
51537                 value: r.data[field],
51538                 row: row,
51539                 column: col,
51540                 cancel:false 
51541             };
51542             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
51543                 this.editing = true;
51544                 var ed = this.colModel.getCellEditor(col, row);
51545                 
51546                 if (!ed) {
51547                     return;
51548                 }
51549                 if(!ed.rendered){
51550                     ed.render(ed.parentEl || document.body);
51551                 }
51552                 ed.field.reset();
51553                
51554                 cell.hide();
51555                 
51556                 (function(){ // complex but required for focus issues in safari, ie and opera
51557                     ed.row = row;
51558                     ed.col = col;
51559                     ed.record = r;
51560                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
51561                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
51562                     this.activeEditor = ed;
51563                     var v = r.data[field];
51564                     ed.startEdit(this.view.getCell(row, col), v);
51565                     // combo's with 'displayField and name set
51566                     if (ed.field.displayField && ed.field.name) {
51567                         ed.field.el.dom.value = r.data[ed.field.name];
51568                     }
51569                     
51570                     
51571                 }).defer(50, this);
51572             }
51573         }
51574     },
51575         
51576     /**
51577      * Stops any active editing
51578      */
51579     stopEditing : function(){
51580         if(this.activeEditor){
51581             this.activeEditor.completeEdit();
51582         }
51583         this.activeEditor = null;
51584     }
51585 });/*
51586  * Based on:
51587  * Ext JS Library 1.1.1
51588  * Copyright(c) 2006-2007, Ext JS, LLC.
51589  *
51590  * Originally Released Under LGPL - original licence link has changed is not relivant.
51591  *
51592  * Fork - LGPL
51593  * <script type="text/javascript">
51594  */
51595
51596 // private - not really -- you end up using it !
51597 // This is a support class used internally by the Grid components
51598
51599 /**
51600  * @class Roo.grid.GridEditor
51601  * @extends Roo.Editor
51602  * Class for creating and editable grid elements.
51603  * @param {Object} config any settings (must include field)
51604  */
51605 Roo.grid.GridEditor = function(field, config){
51606     if (!config && field.field) {
51607         config = field;
51608         field = Roo.factory(config.field, Roo.form);
51609     }
51610     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
51611     field.monitorTab = false;
51612 };
51613
51614 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
51615     
51616     /**
51617      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
51618      */
51619     
51620     alignment: "tl-tl",
51621     autoSize: "width",
51622     hideEl : false,
51623     cls: "x-small-editor x-grid-editor",
51624     shim:false,
51625     shadow:"frame"
51626 });/*
51627  * Based on:
51628  * Ext JS Library 1.1.1
51629  * Copyright(c) 2006-2007, Ext JS, LLC.
51630  *
51631  * Originally Released Under LGPL - original licence link has changed is not relivant.
51632  *
51633  * Fork - LGPL
51634  * <script type="text/javascript">
51635  */
51636   
51637
51638   
51639 Roo.grid.PropertyRecord = Roo.data.Record.create([
51640     {name:'name',type:'string'},  'value'
51641 ]);
51642
51643
51644 Roo.grid.PropertyStore = function(grid, source){
51645     this.grid = grid;
51646     this.store = new Roo.data.Store({
51647         recordType : Roo.grid.PropertyRecord
51648     });
51649     this.store.on('update', this.onUpdate,  this);
51650     if(source){
51651         this.setSource(source);
51652     }
51653     Roo.grid.PropertyStore.superclass.constructor.call(this);
51654 };
51655
51656
51657
51658 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
51659     setSource : function(o){
51660         this.source = o;
51661         this.store.removeAll();
51662         var data = [];
51663         for(var k in o){
51664             if(this.isEditableValue(o[k])){
51665                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
51666             }
51667         }
51668         this.store.loadRecords({records: data}, {}, true);
51669     },
51670
51671     onUpdate : function(ds, record, type){
51672         if(type == Roo.data.Record.EDIT){
51673             var v = record.data['value'];
51674             var oldValue = record.modified['value'];
51675             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
51676                 this.source[record.id] = v;
51677                 record.commit();
51678                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
51679             }else{
51680                 record.reject();
51681             }
51682         }
51683     },
51684
51685     getProperty : function(row){
51686        return this.store.getAt(row);
51687     },
51688
51689     isEditableValue: function(val){
51690         if(val && val instanceof Date){
51691             return true;
51692         }else if(typeof val == 'object' || typeof val == 'function'){
51693             return false;
51694         }
51695         return true;
51696     },
51697
51698     setValue : function(prop, value){
51699         this.source[prop] = value;
51700         this.store.getById(prop).set('value', value);
51701     },
51702
51703     getSource : function(){
51704         return this.source;
51705     }
51706 });
51707
51708 Roo.grid.PropertyColumnModel = function(grid, store){
51709     this.grid = grid;
51710     var g = Roo.grid;
51711     g.PropertyColumnModel.superclass.constructor.call(this, [
51712         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
51713         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
51714     ]);
51715     this.store = store;
51716     this.bselect = Roo.DomHelper.append(document.body, {
51717         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
51718             {tag: 'option', value: 'true', html: 'true'},
51719             {tag: 'option', value: 'false', html: 'false'}
51720         ]
51721     });
51722     Roo.id(this.bselect);
51723     var f = Roo.form;
51724     this.editors = {
51725         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
51726         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
51727         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
51728         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
51729         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
51730     };
51731     this.renderCellDelegate = this.renderCell.createDelegate(this);
51732     this.renderPropDelegate = this.renderProp.createDelegate(this);
51733 };
51734
51735 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
51736     
51737     
51738     nameText : 'Name',
51739     valueText : 'Value',
51740     
51741     dateFormat : 'm/j/Y',
51742     
51743     
51744     renderDate : function(dateVal){
51745         return dateVal.dateFormat(this.dateFormat);
51746     },
51747
51748     renderBool : function(bVal){
51749         return bVal ? 'true' : 'false';
51750     },
51751
51752     isCellEditable : function(colIndex, rowIndex){
51753         return colIndex == 1;
51754     },
51755
51756     getRenderer : function(col){
51757         return col == 1 ?
51758             this.renderCellDelegate : this.renderPropDelegate;
51759     },
51760
51761     renderProp : function(v){
51762         return this.getPropertyName(v);
51763     },
51764
51765     renderCell : function(val){
51766         var rv = val;
51767         if(val instanceof Date){
51768             rv = this.renderDate(val);
51769         }else if(typeof val == 'boolean'){
51770             rv = this.renderBool(val);
51771         }
51772         return Roo.util.Format.htmlEncode(rv);
51773     },
51774
51775     getPropertyName : function(name){
51776         var pn = this.grid.propertyNames;
51777         return pn && pn[name] ? pn[name] : name;
51778     },
51779
51780     getCellEditor : function(colIndex, rowIndex){
51781         var p = this.store.getProperty(rowIndex);
51782         var n = p.data['name'], val = p.data['value'];
51783         
51784         if(typeof(this.grid.customEditors[n]) == 'string'){
51785             return this.editors[this.grid.customEditors[n]];
51786         }
51787         if(typeof(this.grid.customEditors[n]) != 'undefined'){
51788             return this.grid.customEditors[n];
51789         }
51790         if(val instanceof Date){
51791             return this.editors['date'];
51792         }else if(typeof val == 'number'){
51793             return this.editors['number'];
51794         }else if(typeof val == 'boolean'){
51795             return this.editors['boolean'];
51796         }else{
51797             return this.editors['string'];
51798         }
51799     }
51800 });
51801
51802 /**
51803  * @class Roo.grid.PropertyGrid
51804  * @extends Roo.grid.EditorGrid
51805  * This class represents the  interface of a component based property grid control.
51806  * <br><br>Usage:<pre><code>
51807  var grid = new Roo.grid.PropertyGrid("my-container-id", {
51808       
51809  });
51810  // set any options
51811  grid.render();
51812  * </code></pre>
51813   
51814  * @constructor
51815  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51816  * The container MUST have some type of size defined for the grid to fill. The container will be
51817  * automatically set to position relative if it isn't already.
51818  * @param {Object} config A config object that sets properties on this grid.
51819  */
51820 Roo.grid.PropertyGrid = function(container, config){
51821     config = config || {};
51822     var store = new Roo.grid.PropertyStore(this);
51823     this.store = store;
51824     var cm = new Roo.grid.PropertyColumnModel(this, store);
51825     store.store.sort('name', 'ASC');
51826     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
51827         ds: store.store,
51828         cm: cm,
51829         enableColLock:false,
51830         enableColumnMove:false,
51831         stripeRows:false,
51832         trackMouseOver: false,
51833         clicksToEdit:1
51834     }, config));
51835     this.getGridEl().addClass('x-props-grid');
51836     this.lastEditRow = null;
51837     this.on('columnresize', this.onColumnResize, this);
51838     this.addEvents({
51839          /**
51840              * @event beforepropertychange
51841              * Fires before a property changes (return false to stop?)
51842              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51843              * @param {String} id Record Id
51844              * @param {String} newval New Value
51845          * @param {String} oldval Old Value
51846              */
51847         "beforepropertychange": true,
51848         /**
51849              * @event propertychange
51850              * Fires after a property changes
51851              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
51852              * @param {String} id Record Id
51853              * @param {String} newval New Value
51854          * @param {String} oldval Old Value
51855              */
51856         "propertychange": true
51857     });
51858     this.customEditors = this.customEditors || {};
51859 };
51860 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
51861     
51862      /**
51863      * @cfg {Object} customEditors map of colnames=> custom editors.
51864      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
51865      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
51866      * false disables editing of the field.
51867          */
51868     
51869       /**
51870      * @cfg {Object} propertyNames map of property Names to their displayed value
51871          */
51872     
51873     render : function(){
51874         Roo.grid.PropertyGrid.superclass.render.call(this);
51875         this.autoSize.defer(100, this);
51876     },
51877
51878     autoSize : function(){
51879         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
51880         if(this.view){
51881             this.view.fitColumns();
51882         }
51883     },
51884
51885     onColumnResize : function(){
51886         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
51887         this.autoSize();
51888     },
51889     /**
51890      * Sets the data for the Grid
51891      * accepts a Key => Value object of all the elements avaiable.
51892      * @param {Object} data  to appear in grid.
51893      */
51894     setSource : function(source){
51895         this.store.setSource(source);
51896         //this.autoSize();
51897     },
51898     /**
51899      * Gets all the data from the grid.
51900      * @return {Object} data  data stored in grid
51901      */
51902     getSource : function(){
51903         return this.store.getSource();
51904     }
51905 });/*
51906  * Based on:
51907  * Ext JS Library 1.1.1
51908  * Copyright(c) 2006-2007, Ext JS, LLC.
51909  *
51910  * Originally Released Under LGPL - original licence link has changed is not relivant.
51911  *
51912  * Fork - LGPL
51913  * <script type="text/javascript">
51914  */
51915  
51916 /**
51917  * @class Roo.LoadMask
51918  * A simple utility class for generically masking elements while loading data.  If the element being masked has
51919  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
51920  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
51921  * element's UpdateManager load indicator and will be destroyed after the initial load.
51922  * @constructor
51923  * Create a new LoadMask
51924  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
51925  * @param {Object} config The config object
51926  */
51927 Roo.LoadMask = function(el, config){
51928     this.el = Roo.get(el);
51929     Roo.apply(this, config);
51930     if(this.store){
51931         this.store.on('beforeload', this.onBeforeLoad, this);
51932         this.store.on('load', this.onLoad, this);
51933         this.store.on('loadexception', this.onLoad, this);
51934         this.removeMask = false;
51935     }else{
51936         var um = this.el.getUpdateManager();
51937         um.showLoadIndicator = false; // disable the default indicator
51938         um.on('beforeupdate', this.onBeforeLoad, this);
51939         um.on('update', this.onLoad, this);
51940         um.on('failure', this.onLoad, this);
51941         this.removeMask = true;
51942     }
51943 };
51944
51945 Roo.LoadMask.prototype = {
51946     /**
51947      * @cfg {Boolean} removeMask
51948      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
51949      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
51950      */
51951     /**
51952      * @cfg {String} msg
51953      * The text to display in a centered loading message box (defaults to 'Loading...')
51954      */
51955     msg : 'Loading...',
51956     /**
51957      * @cfg {String} msgCls
51958      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
51959      */
51960     msgCls : 'x-mask-loading',
51961
51962     /**
51963      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
51964      * @type Boolean
51965      */
51966     disabled: false,
51967
51968     /**
51969      * Disables the mask to prevent it from being displayed
51970      */
51971     disable : function(){
51972        this.disabled = true;
51973     },
51974
51975     /**
51976      * Enables the mask so that it can be displayed
51977      */
51978     enable : function(){
51979         this.disabled = false;
51980     },
51981
51982     // private
51983     onLoad : function(){
51984         this.el.unmask(this.removeMask);
51985     },
51986
51987     // private
51988     onBeforeLoad : function(){
51989         if(!this.disabled){
51990             this.el.mask(this.msg, this.msgCls);
51991         }
51992     },
51993
51994     // private
51995     destroy : function(){
51996         if(this.store){
51997             this.store.un('beforeload', this.onBeforeLoad, this);
51998             this.store.un('load', this.onLoad, this);
51999             this.store.un('loadexception', this.onLoad, this);
52000         }else{
52001             var um = this.el.getUpdateManager();
52002             um.un('beforeupdate', this.onBeforeLoad, this);
52003             um.un('update', this.onLoad, this);
52004             um.un('failure', this.onLoad, this);
52005         }
52006     }
52007 };/*
52008  * Based on:
52009  * Ext JS Library 1.1.1
52010  * Copyright(c) 2006-2007, Ext JS, LLC.
52011  *
52012  * Originally Released Under LGPL - original licence link has changed is not relivant.
52013  *
52014  * Fork - LGPL
52015  * <script type="text/javascript">
52016  */
52017 Roo.XTemplate = function(){
52018     Roo.XTemplate.superclass.constructor.apply(this, arguments);
52019     var s = this.html;
52020
52021     s = ['<tpl>', s, '</tpl>'].join('');
52022
52023     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
52024
52025     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
52026     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
52027     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
52028     var m, id = 0;
52029     var tpls = [];
52030
52031     while(m = s.match(re)){
52032        var m2 = m[0].match(nameRe);
52033        var m3 = m[0].match(ifRe);
52034        var m4 = m[0].match(execRe);
52035        var exp = null, fn = null, exec = null;
52036        var name = m2 && m2[1] ? m2[1] : '';
52037        if(m3){
52038            exp = m3 && m3[1] ? m3[1] : null;
52039            if(exp){
52040                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
52041            }
52042        }
52043        if(m4){
52044            exp = m4 && m4[1] ? m4[1] : null;
52045            if(exp){
52046                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
52047            }
52048        }
52049        if(name){
52050            switch(name){
52051                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
52052                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
52053                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
52054            }
52055        }
52056        tpls.push({
52057             id: id,
52058             target: name,
52059             exec: exec,
52060             test: fn,
52061             body: m[1]||''
52062         });
52063        s = s.replace(m[0], '{xtpl'+ id + '}');
52064        ++id;
52065     }
52066     for(var i = tpls.length-1; i >= 0; --i){
52067         this.compileTpl(tpls[i]);
52068     }
52069     this.master = tpls[tpls.length-1];
52070     this.tpls = tpls;
52071 };
52072 Roo.extend(Roo.XTemplate, Roo.Template, {
52073
52074     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
52075
52076     applySubTemplate : function(id, values, parent){
52077         var t = this.tpls[id];
52078         if(t.test && !t.test.call(this, values, parent)){
52079             return '';
52080         }
52081         if(t.exec && t.exec.call(this, values, parent)){
52082             return '';
52083         }
52084         var vs = t.target ? t.target.call(this, values, parent) : values;
52085         parent = t.target ? values : parent;
52086         if(t.target && vs instanceof Array){
52087             var buf = [];
52088             for(var i = 0, len = vs.length; i < len; i++){
52089                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
52090             }
52091             return buf.join('');
52092         }
52093         return t.compiled.call(this, vs, parent);
52094     },
52095
52096     compileTpl : function(tpl){
52097         var fm = Roo.util.Format;
52098         var useF = this.disableFormats !== true;
52099         var sep = Roo.isGecko ? "+" : ",";
52100         var fn = function(m, name, format, args){
52101             if(name.substr(0, 4) == 'xtpl'){
52102                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
52103             }
52104             var v;
52105             if(name.indexOf('.') != -1){
52106                 v = name;
52107             }else{
52108                 v = "values['" + name + "']";
52109             }
52110             if(format && useF){
52111                 args = args ? ',' + args : "";
52112                 if(format.substr(0, 5) != "this."){
52113                     format = "fm." + format + '(';
52114                 }else{
52115                     format = 'this.call("'+ format.substr(5) + '", ';
52116                     args = ", values";
52117                 }
52118             }else{
52119                 args= ''; format = "("+v+" === undefined ? '' : ";
52120             }
52121             return "'"+ sep + format + v + args + ")"+sep+"'";
52122         };
52123         var body;
52124         // branched to use + in gecko and [].join() in others
52125         if(Roo.isGecko){
52126             body = "tpl.compiled = function(values, parent){ return '" +
52127                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
52128                     "';};";
52129         }else{
52130             body = ["tpl.compiled = function(values, parent){ return ['"];
52131             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
52132             body.push("'].join('');};");
52133             body = body.join('');
52134         }
52135         /** eval:var:zzzzzzz */
52136         eval(body);
52137         return this;
52138     },
52139
52140     applyTemplate : function(values){
52141         return this.master.compiled.call(this, values, {});
52142         var s = this.subs;
52143     },
52144
52145     apply : function(){
52146         return this.applyTemplate.apply(this, arguments);
52147     },
52148
52149     compile : function(){return this;}
52150 });
52151
52152 Roo.XTemplate.from = function(el){
52153     el = Roo.getDom(el);
52154     return new Roo.XTemplate(el.value || el.innerHTML);
52155 };/*
52156  * Original code for Roojs - LGPL
52157  * <script type="text/javascript">
52158  */
52159  
52160 /**
52161  * @class Roo.XComponent
52162  * A delayed Element creator...
52163  * Or a way to group chunks of interface together.
52164  * 
52165  * Mypart.xyx = new Roo.XComponent({
52166
52167     parent : 'Mypart.xyz', // empty == document.element.!!
52168     order : '001',
52169     name : 'xxxx'
52170     region : 'xxxx'
52171     disabled : function() {} 
52172      
52173     tree : function() { // return an tree of xtype declared components
52174         var MODULE = this;
52175         return 
52176         {
52177             xtype : 'NestedLayoutPanel',
52178             // technicall
52179         }
52180      ]
52181  *})
52182  *
52183  *
52184  * It can be used to build a big heiracy, with parent etc.
52185  * or you can just use this to render a single compoent to a dom element
52186  * MYPART.render(Roo.Element | String(id) | dom_element )
52187  * 
52188  * @extends Roo.util.Observable
52189  * @constructor
52190  * @param cfg {Object} configuration of component
52191  * 
52192  */
52193 Roo.XComponent = function(cfg) {
52194     Roo.apply(this, cfg);
52195     this.addEvents({ 
52196         /**
52197              * @event built
52198              * Fires when this the componnt is built
52199              * @param {Roo.XComponent} c the component
52200              */
52201         'built' : true,
52202         /**
52203              * @event buildcomplete
52204              * Fires on the top level element when all elements have been built
52205              * @param {Roo.XComponent} c the top level component.
52206          */
52207         'buildcomplete' : true
52208         
52209     });
52210     this.region = this.region || 'center'; // default..
52211     Roo.XComponent.register(this);
52212     this.modules = false;
52213     this.el = false; // where the layout goes..
52214     
52215     
52216 }
52217 Roo.extend(Roo.XComponent, Roo.util.Observable, {
52218     /**
52219      * @property el
52220      * The created element (with Roo.factory())
52221      * @type {Roo.Layout}
52222      */
52223     el  : false,
52224     
52225     /**
52226      * @property el
52227      * for BC  - use el in new code
52228      * @type {Roo.Layout}
52229      */
52230     panel : false,
52231     
52232     /**
52233      * @property layout
52234      * for BC  - use el in new code
52235      * @type {Roo.Layout}
52236      */
52237     layout : false,
52238     
52239      /**
52240      * @cfg {Function|boolean} disabled
52241      * If this module is disabled by some rule, return true from the funtion
52242      */
52243     disabled : false,
52244     
52245     /**
52246      * @cfg {String} parent 
52247      * Name of parent element which it get xtype added to..
52248      */
52249     parent: false,
52250     
52251     /**
52252      * @cfg {String} order
52253      * Used to set the order in which elements are created (usefull for multiple tabs)
52254      */
52255     
52256     order : false,
52257     /**
52258      * @cfg {String} name
52259      * String to display while loading.
52260      */
52261     name : false,
52262     /**
52263      * @cfg {String} region
52264      * Region to render component to (defaults to center)
52265      */
52266     region : 'center',
52267     
52268     /**
52269      * @cfg {Array} items
52270      * A single item array - the first element is the root of the tree..
52271      * It's done this way to stay compatible with the Xtype system...
52272      */
52273     items : false,
52274     
52275     
52276      /**
52277      * render
52278      * render element to dom or tree
52279      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
52280      */
52281     
52282     render : function(el)
52283     {
52284         
52285         el = el || false;
52286         var hp = this.parent ? 1 : 0;
52287         
52288         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
52289             // if parent is a '#.....' string, then let's use that..
52290             var ename = this.parent.substr(1)
52291             this.parent = false;
52292             el = Roo.get(ename);
52293             if (!el) {
52294                 Roo.log("Warning - element can not be found :#" + ename );
52295                 return;
52296             }
52297         }
52298         
52299         
52300         if (!this.parent) {
52301             
52302             el = el ? Roo.get(el) : false;
52303             
52304             // it's a top level one..
52305             this.parent =  {
52306                 el : new Roo.BorderLayout(el || document.body, {
52307                 
52308                      center: {
52309                          titlebar: false,
52310                          autoScroll:false,
52311                          closeOnTab: true,
52312                          tabPosition: 'top',
52313                           //resizeTabs: true,
52314                          alwaysShowTabs: el && hp? false :  true,
52315                          hideTabs: el || !hp ? true :  false,
52316                          minTabWidth: 140
52317                      }
52318                  })
52319             }
52320         }
52321         
52322         
52323             
52324         var tree = this.tree();
52325         tree.region = tree.region || this.region;
52326         this.el = this.parent.el.addxtype(tree);
52327         this.fireEvent('built', this);
52328         
52329         this.panel = this.el;
52330         this.layout = this.panel.layout;    
52331          
52332     }
52333     
52334 });
52335
52336 Roo.apply(Roo.XComponent, {
52337     
52338     /**
52339      * @property  buildCompleted
52340      * True when the builder has completed building the interface.
52341      * @type Boolean
52342      */
52343     buildCompleted : false,
52344      
52345     /**
52346      * @property  topModule
52347      * the upper most module - uses document.element as it's constructor.
52348      * @type Object
52349      */
52350      
52351     topModule  : false,
52352       
52353     /**
52354      * @property  modules
52355      * array of modules to be created by registration system.
52356      * @type {Array} of Roo.XComponent
52357      */
52358     
52359     modules : [],
52360     /**
52361      * @property  elmodules
52362      * array of modules to be created by which use #ID 
52363      * @type {Array} of Roo.XComponent
52364      */
52365      
52366     elmodules : [],
52367
52368     
52369     /**
52370      * Register components to be built later.
52371      *
52372      * This solves the following issues
52373      * - Building is not done on page load, but after an authentication process has occured.
52374      * - Interface elements are registered on page load
52375      * - Parent Interface elements may not be loaded before child, so this handles that..
52376      * 
52377      *
52378      * example:
52379      * 
52380      * MyApp.register({
52381           order : '000001',
52382           module : 'Pman.Tab.projectMgr',
52383           region : 'center',
52384           parent : 'Pman.layout',
52385           disabled : false,  // or use a function..
52386         })
52387      
52388      * * @param {Object} details about module
52389      */
52390     register : function(obj) {
52391         this.modules.push(obj);
52392          
52393     },
52394     /**
52395      * convert a string to an object..
52396      * eg. 'AAA.BBB' -> finds AAA.BBB
52397
52398      */
52399     
52400     toObject : function(str)
52401     {
52402         if (!str || typeof(str) == 'object') {
52403             return str;
52404         }
52405         if (str.substring(0,1) == '#') {
52406             return str;
52407         }
52408
52409         var ar = str.split('.');
52410         var rt, o;
52411         rt = ar.shift();
52412             /** eval:var:o */
52413         try {
52414             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
52415         } catch (e) {
52416             throw "Module not found : " + str;
52417         }
52418         
52419         if (o === false) {
52420             throw "Module not found : " + str;
52421         }
52422         Roo.each(ar, function(e) {
52423             if (typeof(o[e]) == 'undefined') {
52424                 throw "Module not found : " + str;
52425             }
52426             o = o[e];
52427         });
52428         
52429         return o;
52430         
52431     },
52432     
52433     
52434     /**
52435      * move modules into their correct place in the tree..
52436      * 
52437      */
52438     preBuild : function ()
52439     {
52440         var _t = this;
52441         Roo.each(this.modules , function (obj)
52442         {
52443             var opar = obj.parent;
52444             try { 
52445                 obj.parent = this.toObject(opar);
52446             } catch(e) {
52447                 Roo.log(e.toString());
52448                 return;
52449             }
52450             
52451             if (!obj.parent) {
52452                 this.topModule = obj;
52453                 return;
52454             }
52455             if (typeof(obj.parent) == 'string') {
52456                 this.elmodules.push(obj);
52457                 return;
52458             }
52459             if (obj.parent.constructor != Roo.XComponent) {
52460                 Roo.log("Object Parent is not instance of XComponent:" + obj.name)
52461             }
52462             if (!obj.parent.modules) {
52463                 obj.parent.modules = new Roo.util.MixedCollection(false, 
52464                     function(o) { return o.order + '' }
52465                 );
52466             }
52467             
52468             obj.parent.modules.add(obj);
52469         }, this);
52470     },
52471     
52472      /**
52473      * make a list of modules to build.
52474      * @return {Array} list of modules. 
52475      */ 
52476     
52477     buildOrder : function()
52478     {
52479         var _this = this;
52480         var cmp = function(a,b) {   
52481             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
52482         };
52483         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
52484             throw "No top level modules to build";
52485         }
52486         
52487         // make a flat list in order of modules to build.
52488         var mods = this.topModule ? [ this.topModule ] : [];
52489         Roo.each(this.elmodules,function(e) { mods.push(e) });
52490
52491         
52492         // add modules to their parents..
52493         var addMod = function(m) {
52494            // Roo.debug && Roo.log(m.modKey);
52495             
52496             mods.push(m);
52497             if (m.modules) {
52498                 m.modules.keySort('ASC',  cmp );
52499                 m.modules.each(addMod);
52500             }
52501             // not sure if this is used any more..
52502             if (m.finalize) {
52503                 m.finalize.name = m.name + " (clean up) ";
52504                 mods.push(m.finalize);
52505             }
52506             
52507         }
52508         if (this.topModule) { 
52509             this.topModule.modules.keySort('ASC',  cmp );
52510             this.topModule.modules.each(addMod);
52511         }
52512         return mods;
52513     },
52514     
52515      /**
52516      * Build the registered modules.
52517      * @param {Object} parent element.
52518      * @param {Function} optional method to call after module has been added.
52519      * 
52520      */ 
52521    
52522     build : function() 
52523     {
52524         
52525         this.preBuild();
52526         var mods = this.buildOrder();
52527       
52528         //this.allmods = mods;
52529         //Roo.debug && Roo.log(mods);
52530         //return;
52531         if (!mods.length) { // should not happen
52532             throw "NO modules!!!";
52533         }
52534         
52535         
52536         
52537         // flash it up as modal - so we store the mask!?
52538         Roo.MessageBox.show({ title: 'loading' });
52539         Roo.MessageBox.show({
52540            title: "Please wait...",
52541            msg: "Building Interface...",
52542            width:450,
52543            progress:true,
52544            closable:false,
52545            modal: false
52546           
52547         });
52548         var total = mods.length;
52549         
52550         var _this = this;
52551         var progressRun = function() {
52552             if (!mods.length) {
52553                 Roo.debug && Roo.log('hide?');
52554                 Roo.MessageBox.hide();
52555                 if (_this.topModule) { 
52556                     _this.topModule.fireEvent('buildcomplete', _this.topModule);
52557                 }
52558                 // THE END...
52559                 return false;   
52560             }
52561             
52562             var m = mods.shift();
52563             
52564             
52565             Roo.debug && Roo.log(m);
52566             // not sure if this is supported any more.. - modules that are are just function
52567             if (typeof(m) == 'function') { 
52568                 m.call(this);
52569                 return progressRun.defer(10, _this);
52570             } 
52571             
52572             
52573             
52574             Roo.MessageBox.updateProgress(
52575                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
52576                     " of " + total + 
52577                     (m.name ? (' - ' + m.name) : '')
52578                     );
52579             
52580          
52581             // is the module disabled?
52582             var disabled = (typeof(m.disabled) == 'function') ?
52583                 m.disabled.call(m.module.disabled) : m.disabled;    
52584             
52585             
52586             if (disabled) {
52587                 return progressRun(); // we do not update the display!
52588             }
52589             
52590             // now build 
52591             
52592             m.render();
52593             // it's 10 on top level, and 1 on others??? why...
52594             return progressRun.defer(10, _this);
52595              
52596         }
52597         progressRun.defer(1, _this);
52598      
52599         
52600         
52601     }
52602     
52603      
52604    
52605     
52606     
52607 });
52608  //<script type="text/javascript">
52609
52610
52611 /**
52612  * @class Roo.Login
52613  * @extends Roo.LayoutDialog
52614  * A generic Login Dialog..... - only one needed in theory!?!?
52615  *
52616  * Fires XComponent builder on success...
52617  * 
52618  * Sends 
52619  *    username,password, lang = for login actions.
52620  *    check = 1 for periodic checking that sesion is valid.
52621  *    passwordRequest = email request password
52622  *    logout = 1 = to logout
52623  * 
52624  * Affects: (this id="????" elements)
52625  *   loading  (removed) (used to indicate application is loading)
52626  *   loading-mask (hides) (used to hide application when it's building loading)
52627  *   
52628  * 
52629  * Usage: 
52630  *    
52631  * 
52632  * Myapp.login = Roo.Login({
52633      url: xxxx,
52634    
52635      realm : 'Myapp', 
52636      
52637      
52638      method : 'POST',
52639      
52640      
52641      * 
52642  })
52643  * 
52644  * 
52645  * 
52646  **/
52647  
52648 Roo.Login = function(cfg)
52649 {
52650     this.addEvents({
52651         'refreshed' : true
52652     });
52653     
52654     Roo.apply(this,cfg);
52655     
52656     Roo.onReady(function() {
52657         this.onLoad();
52658     }, this);
52659     // call parent..
52660     
52661    
52662     Roo.Login.superclass.constructor.call(this, this);
52663     //this.addxtype(this.items[0]);
52664     
52665     
52666 }
52667
52668
52669 Roo.extend(Roo.Login, Roo.LayoutDialog, {
52670     
52671     /**
52672      * @cfg {String} method
52673      * Method used to query for login details.
52674      */
52675     
52676     method : 'POST',
52677     /**
52678      * @cfg {String} url
52679      * URL to query login data. - eg. baseURL + '/Login.php'
52680      */
52681     url : '',
52682     
52683     /**
52684      * @property user
52685      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
52686      * @type {Object} 
52687      */
52688     user : false,
52689     /**
52690      * @property checkFails
52691      * Number of times we have attempted to get authentication check, and failed.
52692      * @type {Number} 
52693      */
52694     checkFails : 0,
52695       /**
52696      * @property intervalID
52697      * The window interval that does the constant login checking.
52698      * @type {Number} 
52699      */
52700     intervalID : 0,
52701     
52702     
52703     onLoad : function() // called on page load...
52704     {
52705         // load 
52706          
52707         if (Roo.get('loading')) { // clear any loading indicator..
52708             Roo.get('loading').remove();
52709         }
52710         
52711         //this.switchLang('en'); // set the language to english..
52712        
52713         this.check({
52714             success:  function(response, opts)  {  // check successfull...
52715             
52716                 var res = this.processResponse(response);
52717                 this.checkFails =0;
52718                 if (!res.success) { // error!
52719                     this.checkFails = 5;
52720                     //console.log('call failure');
52721                     return this.failure(response,opts);
52722                 }
52723                 
52724                 if (!res.data.id) { // id=0 == login failure.
52725                     return this.show();
52726                 }
52727                 
52728                               
52729                         //console.log(success);
52730                 this.fillAuth(res.data);   
52731                 this.checkFails =0;
52732                 Roo.XComponent.build();
52733             },
52734             failure : this.show
52735         });
52736         
52737     }, 
52738     
52739     
52740     check: function(cfg) // called every so often to refresh cookie etc..
52741     {
52742         if (cfg.again) { // could be undefined..
52743             this.checkFails++;
52744         } else {
52745             this.checkFails = 0;
52746         }
52747         var _this = this;
52748         if (this.sending) {
52749             if ( this.checkFails > 4) {
52750                 Roo.MessageBox.alert("Error",  
52751                     "Error getting authentication status. - try reloading, or wait a while", function() {
52752                         _this.sending = false;
52753                     }); 
52754                 return;
52755             }
52756             cfg.again = true;
52757             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
52758             return;
52759         }
52760         this.sending = true;
52761         
52762         Roo.Ajax.request({  
52763             url: this.url,
52764             params: {
52765                 getAuthUser: true
52766             },  
52767             method: this.method,
52768             success:  cfg.success || this.success,
52769             failure : cfg.failure || this.failure,
52770             scope : this,
52771             callCfg : cfg
52772               
52773         });  
52774     }, 
52775     
52776     
52777     logout: function()
52778     {
52779         window.onbeforeunload = function() { }; // false does not work for IE..
52780         this.user = false;
52781         var _this = this;
52782         
52783         Roo.Ajax.request({  
52784             url: this.url,
52785             params: {
52786                 logout: 1
52787             },  
52788             method: 'GET',
52789             failure : function() {
52790                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
52791                     document.location = document.location.toString() + '?ts=' + Math.random();
52792                 });
52793                 
52794             },
52795             success : function() {
52796                 _this.user = false;
52797                 this.checkFails =0;
52798                 // fixme..
52799                 document.location = document.location.toString() + '?ts=' + Math.random();
52800             }
52801               
52802               
52803         }); 
52804     },
52805     
52806     processResponse : function (response)
52807     {
52808         var res = '';
52809         try {
52810             res = Roo.decode(response.responseText);
52811             // oops...
52812             if (typeof(res) != 'object') {
52813                 res = { success : false, errorMsg : res, errors : true };
52814             }
52815             if (typeof(res.success) == 'undefined') {
52816                 res.success = false;
52817             }
52818             
52819         } catch(e) {
52820             res = { success : false,  errorMsg : response.responseText, errors : true };
52821         }
52822         return res;
52823     },
52824     
52825     success : function(response, opts)  // check successfull...
52826     {  
52827         this.sending = false;
52828         var res = this.processResponse(response);
52829         if (!res.success) {
52830             return this.failure(response, opts);
52831         }
52832         if (!res.data || !res.data.id) {
52833             return this.failure(response,opts);
52834         }
52835         //console.log(res);
52836         this.fillAuth(res.data);
52837         
52838         this.checkFails =0;
52839         
52840     },
52841     
52842     
52843     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
52844     {
52845         this.authUser = -1;
52846         this.sending = false;
52847         var res = this.processResponse(response);
52848         //console.log(res);
52849         if ( this.checkFails > 2) {
52850         
52851             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
52852                 "Error getting authentication status. - try reloading"); 
52853             return;
52854         }
52855         opts.callCfg.again = true;
52856         this.check.defer(1000, this, [ opts.callCfg ]);
52857         return;  
52858     },
52859     
52860     
52861     
52862     fillAuth: function(au) {
52863         this.startAuthCheck();
52864         this.authUserId = au.id;
52865         this.authUser = au;
52866         this.lastChecked = new Date();
52867         this.fireEvent('refreshed', au);
52868         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
52869         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
52870         au.lang = au.lang || 'en';
52871         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
52872         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
52873         this.switchLang(au.lang );
52874         
52875      
52876         // open system... - -on setyp..
52877         if (this.authUserId  < 0) {
52878             Roo.MessageBox.alert("Warning", 
52879                 "This is an open system - please set up a admin user with a password.");  
52880         }
52881          
52882         //Pman.onload(); // which should do nothing if it's a re-auth result...
52883         
52884              
52885     },
52886     
52887     startAuthCheck : function() // starter for timeout checking..
52888     {
52889         if (this.intervalID) { // timer already in place...
52890             return false;
52891         }
52892         var _this = this;
52893         this.intervalID =  window.setInterval(function() {
52894               _this.check(false);
52895             }, 120000); // every 120 secs = 2mins..
52896         
52897         
52898     },
52899          
52900     
52901     switchLang : function (lang) 
52902     {
52903         _T = typeof(_T) == 'undefined' ? false : _T;
52904           if (!_T || !lang.length) {
52905             return;
52906         }
52907         
52908         if (!_T && lang != 'en') {
52909             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52910             return;
52911         }
52912         
52913         if (typeof(_T.en) == 'undefined') {
52914             _T.en = {};
52915             Roo.apply(_T.en, _T);
52916         }
52917         
52918         if (typeof(_T[lang]) == 'undefined') {
52919             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
52920             return;
52921         }
52922         
52923         
52924         Roo.apply(_T, _T[lang]);
52925         // just need to set the text values for everything...
52926         var _this = this;
52927         /* this will not work ...
52928         if (this.form) { 
52929             
52930                
52931             function formLabel(name, val) {
52932                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
52933             }
52934             
52935             formLabel('password', "Password"+':');
52936             formLabel('username', "Email Address"+':');
52937             formLabel('lang', "Language"+':');
52938             this.dialog.setTitle("Login");
52939             this.dialog.buttons[0].setText("Forgot Password");
52940             this.dialog.buttons[1].setText("Login");
52941         }
52942         */
52943         
52944         
52945     },
52946     
52947     
52948     title: "Login",
52949     modal: true,
52950     width:  350,
52951     //height: 230,
52952     height: 180,
52953     shadow: true,
52954     minWidth:200,
52955     minHeight:180,
52956     //proxyDrag: true,
52957     closable: false,
52958     draggable: false,
52959     collapsible: false,
52960     resizable: false,
52961     center: {  // needed??
52962         autoScroll:false,
52963         titlebar: false,
52964        // tabPosition: 'top',
52965         hideTabs: true,
52966         closeOnTab: true,
52967         alwaysShowTabs: false
52968     } ,
52969     listeners : {
52970         
52971         show  : function(dlg)
52972         {
52973             //console.log(this);
52974             this.form = this.layout.getRegion('center').activePanel.form;
52975             this.form.dialog = dlg;
52976             this.buttons[0].form = this.form;
52977             this.buttons[0].dialog = dlg;
52978             this.buttons[1].form = this.form;
52979             this.buttons[1].dialog = dlg;
52980            
52981            //this.resizeToLogo.defer(1000,this);
52982             // this is all related to resizing for logos..
52983             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
52984            //// if (!sz) {
52985              //   this.resizeToLogo.defer(1000,this);
52986              //   return;
52987            // }
52988             //var w = Ext.lib.Dom.getViewWidth() - 100;
52989             //var h = Ext.lib.Dom.getViewHeight() - 100;
52990             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
52991             //this.center();
52992             if (this.disabled) {
52993                 this.hide();
52994                 return;
52995             }
52996             
52997             if (this.user.id < 0) { // used for inital setup situations.
52998                 return;
52999             }
53000             
53001             if (this.intervalID) {
53002                 // remove the timer
53003                 window.clearInterval(this.intervalID);
53004                 this.intervalID = false;
53005             }
53006             
53007             
53008             if (Roo.get('loading')) {
53009                 Roo.get('loading').remove();
53010             }
53011             if (Roo.get('loading-mask')) {
53012                 Roo.get('loading-mask').hide();
53013             }
53014             
53015             //incomming._node = tnode;
53016             this.form.reset();
53017             //this.dialog.modal = !modal;
53018             //this.dialog.show();
53019             this.el.unmask(); 
53020             
53021             
53022             this.form.setValues({
53023                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
53024                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
53025             });
53026             
53027             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
53028             if (this.form.findField('username').getValue().length > 0 ){
53029                 this.form.findField('password').focus();
53030             } else {
53031                this.form.findField('username').focus();
53032             }
53033     
53034         }
53035     },
53036     items : [
53037          {
53038        
53039             xtype : 'ContentPanel',
53040             xns : Roo,
53041             region: 'center',
53042             fitToFrame : true,
53043             
53044             items : [
53045     
53046                 {
53047                
53048                     xtype : 'Form',
53049                     xns : Roo.form,
53050                     labelWidth: 100,
53051                     style : 'margin: 10px;',
53052                     
53053                     listeners : {
53054                         actionfailed : function(f, act) {
53055                             // form can return { errors: .... }
53056                                 
53057                             //act.result.errors // invalid form element list...
53058                             //act.result.errorMsg// invalid form element list...
53059                             
53060                             this.dialog.el.unmask();
53061                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
53062                                         "Login failed - communication error - try again.");
53063                                       
53064                         },
53065                         actioncomplete: function(re, act) {
53066                              
53067                             Roo.state.Manager.set(
53068                                 this.dialog.realm + '.username',  
53069                                     this.findField('username').getValue()
53070                             );
53071                             Roo.state.Manager.set(
53072                                 this.dialog.realm + '.lang',  
53073                                 this.findField('lang').getValue() 
53074                             );
53075                             
53076                             this.dialog.fillAuth(act.result.data);
53077                               
53078                             this.dialog.hide();
53079                             
53080                             if (Roo.get('loading-mask')) {
53081                                 Roo.get('loading-mask').show();
53082                             }
53083                             Roo.XComponent.build();
53084                             
53085                              
53086                             
53087                         }
53088                     },
53089                     items : [
53090                         {
53091                             xtype : 'TextField',
53092                             xns : Roo.form,
53093                             fieldLabel: "Email Address",
53094                             name: 'username',
53095                             width:200,
53096                             autoCreate : {tag: "input", type: "text", size: "20"}
53097                         },
53098                         {
53099                             xtype : 'TextField',
53100                             xns : Roo.form,
53101                             fieldLabel: "Password",
53102                             inputType: 'password',
53103                             name: 'password',
53104                             width:200,
53105                             autoCreate : {tag: "input", type: "text", size: "20"},
53106                             listeners : {
53107                                 specialkey : function(e,ev) {
53108                                     if (ev.keyCode == 13) {
53109                                         this.form.dialog.el.mask("Logging in");
53110                                         this.form.doAction('submit', {
53111                                             url: this.form.dialog.url,
53112                                             method: this.form.dialog.method
53113                                         });
53114                                     }
53115                                 }
53116                             }  
53117                         },
53118                         {
53119                             xtype : 'ComboBox',
53120                             xns : Roo.form,
53121                             fieldLabel: "Language",
53122                             name : 'langdisp',
53123                             store: {
53124                                 xtype : 'SimpleStore',
53125                                 fields: ['lang', 'ldisp'],
53126                                 data : [
53127                                     [ 'en', 'English' ],
53128                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
53129                                     [ 'zh_CN', '\u7C21\u4E2D' ]
53130                                 ]
53131                             },
53132                             
53133                             valueField : 'lang',
53134                             hiddenName:  'lang',
53135                             width: 200,
53136                             displayField:'ldisp',
53137                             typeAhead: false,
53138                             editable: false,
53139                             mode: 'local',
53140                             triggerAction: 'all',
53141                             emptyText:'Select a Language...',
53142                             selectOnFocus:true,
53143                             listeners : {
53144                                 select :  function(cb, rec, ix) {
53145                                     this.form.switchLang(rec.data.lang);
53146                                 }
53147                             }
53148                         
53149                         }
53150                     ]
53151                 }
53152                   
53153                 
53154             ]
53155         }
53156     ],
53157     buttons : [
53158         {
53159             xtype : 'Button',
53160             xns : 'Roo',
53161             text : "Forgot Password",
53162             listeners : {
53163                 click : function() {
53164                     //console.log(this);
53165                     var n = this.form.findField('username').getValue();
53166                     if (!n.length) {
53167                         Roo.MessageBox.alert("Error", "Fill in your email address");
53168                         return;
53169                     }
53170                     Roo.Ajax.request({
53171                         url: this.dialog.url,
53172                         params: {
53173                             passwordRequest: n
53174                         },
53175                         method: this.dialog.method,
53176                         success:  function(response, opts)  {  // check successfull...
53177                         
53178                             var res = this.dialog.processResponse(response);
53179                             if (!res.success) { // error!
53180                                Roo.MessageBox.alert("Error" ,
53181                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
53182                                return;
53183                             }
53184                             Roo.MessageBox.alert("Notice" ,
53185                                 "Please check you email for the Password Reset message");
53186                         },
53187                         failure : function() {
53188                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
53189                         }
53190                         
53191                     });
53192                 }
53193             }
53194         },
53195         {
53196             xtype : 'Button',
53197             xns : 'Roo',
53198             text : "Login",
53199             listeners : {
53200                 
53201                 click : function () {
53202                         
53203                     this.dialog.el.mask("Logging in");
53204                     this.form.doAction('submit', {
53205                             url: this.dialog.url,
53206                             method: this.dialog.method
53207                     });
53208                 }
53209             }
53210         }
53211     ]
53212   
53213   
53214 })
53215  
53216
53217
53218